#---------------------------------------------------------------------------
# Author          : Manasa Ramakrishna, mr325@le.ac.uk
# Date started  : 4th August, 2017
# Last modified : 4th August, 2017
# Aim             : To take a look at first SILAC labelled LOPIT data on Trizol
# Depends       : On 'silacFunctions.R'. Make sure they are in the same directory
# Notes         : Works on data from Rayner's first experiments
#--------------------------------------------------------------------------- 
# Invoking libraries
library(MSnbase)
library(gplots)
library(reshape2)
library(VIM)
library(zoo) 
library(spatstat) # "im" function 
library(ggbiplot)
library(org.Hs.eg.db)
library(clusterProfiler)
library("biomaRt")
library(goseq)
library(limma)
library(ggplot2)
library(outliers)
library(RColorBrewer)
library(stringr)
#Setting working directories
wd = "/Users/manasa/Documents/Work/TTT/02_Proteomics/02_First-TMT-data/"
setwd(wd)
getwd()
[1] "/Users/manasa/Documents/Work/TTT/02_Proteomics/02_First-TMT-data"
indir = paste(wd,"Input",sep="/")
outdir = paste(wd,paste(Sys.Date(),"Output",sep = "_"),sep = "/")
if (exists(outdir)){
  print("Outdir exists")
}else{
  dir.create(outdir)
}
# Sourcing function file
source("tmtFunctions.R")

Now that we have loaded all the packages we need for working with this data, let’s move on to the data.

# -----------------------------------------------
# Step 00: Read data
# Read in all the data required for analysis
# -----------------------------------------------
# File of contaminants - proteins to exclude from analysis as are things like keratin, alcohol dehydrogenase etc....
contam = read.delim("Input/Common contaminant_all.csv",sep=",",header=T)
# Read in the sample file that matches columns to sample contents
samp.dat = read.delim("Input/samples.txt",sep="\t",header=T)
# Read in the data files that contain peptide level output from Proteome discoverer...
# Note: The columns that begin with "Found.in.Sample.in" correspond to various samples in the study.
# Columns of interest are "sequence", "modifications","master.protein.accessions","abundance","quan.info"
data = read.delim("Input/Dosages_4step-Trizol_PeptideGroups.txt",sep="\t",comment.char="",as.is=T,header=T)
# Subset data to only keep columns of interest
prot.data = data[,c(1:6,10:14,17,42:51,62)]
# Rename tmt tagged columns with UV dosage names
for(i in 1:nrow(samp.dat)){
  id = grep(samp.dat$TMT[i],colnames(prot.data))
  colnames(prot.data)[id] = paste(samp.dat$Sample[i])
}
dim(prot.data)
[1] 14456    23
head(prot.data)

data has 23 columns and 14456 rows - each row belonging to a peptide abundance value across all 10 samples. We now go through a series of filtering steps to obtain a dataset we can use for downstream analyses.

# ---------------------------------------------------------------------------------
# Step 01 : Filter 
# We perform 3 layers of filtering - unique proteins, contaminants,missing values
# ---------------------------------------------------------------------------------
# Step 1a : Filter only for those peptides that have a unique master protein. Done using column "quan.info" and titled 'Unique'
peptide.stats = table(prot.data$Quan.Info)
peptide.stats

NoQuanValues    NotUnique       Unique 
        1440          715        12301 
filt.1a = prot.data[which(prot.data$Quan.Info == "Unique"),]
dim(filt.1a) #12301 are unique peptides, 715 are non-unique and 1440 are missing values 
[1] 12301    23
# Step 1b : Filter out those proteins that are contaminants from the contaminants list and annotate missing values
filt.1b = filt.1a[-which(filt.1a$Master.Protein.Accessions %in% contam$Protein.Group.Accessions),]
num.contams = length(which(filt.1a$Master.Protein.Accessions %in% contam$Protein.Group.Accessions))
dim(filt.1a) # 12301 in total
[1] 12301    23
dim(filt.1b) # 12077 filtered proteins
[1] 12077    23
print(num.contams) # 224 contaminant proteins
[1] 224
# Adding extra information about rows with missing values
filt.1b$count.missing = rowSums(is.na(filt.1b[,c(13:22)]))
filt.1b$Missing = FALSE
filt.1b$Missing[which(filt.1b$count.missing > 0)] = TRUE
head(filt.1b)

We have a column called “Missing” to identify which peptides have one or more missing values across the 10 samples. “count.missing” tells us how many missing values there are for that peptide.

# ---------------------------------------------------------------------------------
# Step 02a : Creating an MSnSet which is needed for using the MSnbase backage
# ---------------------------------------------------------------------------------
# The rownames of samp.dat have to be the same as column names in the expression data matrix
rownames(samp.dat) = samp.dat$Sample
# Create an MSnSet object
res <- MSnSet(exprs = as.matrix(filt.1b[,c(13:22)]),fData=filt.1b[,c(10,1:9,12,23:25)],pData = samp.dat[,c(2,1)])
res <- res[rowSums(is.na(exprs(res)))!=10,] # exclude  peptides without any quantification
print(res)
MSnSet (storageMode: lockedEnvironment)
assayData: 12077 features, 10 samples 
  element names: exprs 
protocolData: none
phenoData
  sampleNames: 150mJ.1 150mJ.2 ... Pool.1 (10 total)
  varLabels: TMT Sample
  varMetadata: labelDescription
featureData
  featureNames: 2 3 ... 14455 (12077 total)
  fvarLabels: Master.Protein.Accessions Checked ... Missing (14 total)
  fvarMetadata: labelDescription
experimentData: use 'experimentData(object)'
Annotation:  
- - - Processing information - - -
Subset [12077,10][12077,10] Wed Sep 13 14:19:33 2017 
 MSnbase version: 2.0.2 
# How many missing values per peptide
table(fData(res)$count.missing)

    0     1     2     3     4     5     6     7     8     9 
11451   551    38    16     8     4     2     4     1     2 
colSums(is.na(exprs(res)))
 150mJ.1 150mJ.2  150mJ.3   275mJ.1  275mJ.2  275mJ.3  400mJ.1  400mJ.2  400mJ.3   Pool.1 
      22       11      612       49        6       17        6       33        6       31 
table(rowSums(is.na(exprs(res))))

    0     1     2     3     4     5     6     7     8     9 
11451   551    38    16     8     4     2     4     1     2 
# Checking missing values
table(is.na(res))

 FALSE   TRUE 
119977    793 

The MSnSet object has been created to include protein abundance values, some metadata and sample information (UV dosage in this case)

# ---------------------------------------------------------------------------------
# Step 02b : Imputing missing values in the protein expression data using 'impute'
# ---------------------------------------------------------------------------------
# Subsetting only those peptides with one or more missing values
# Replacing missing values with 0 and non-missing with 1
# Displaying missing values
miss.many = res[rowSums(is.na(exprs(res)))>=1,]
exprs(miss.many)[exprs(miss.many) != 0] = 1
exprs(miss.many)[is.na(exprs(miss.many))] = 0
heatmap.2(exprs(miss.many), col = c("lightgray", "black"),
            scale = "none", dendrogram = "none",
            trace = "none", keysize = 0.5, key = FALSE,Colv=F,
            ColSideColors = rep(c("steelblue", "darkolivegreen","magenta","black"), times = c(3,3,3,1)))

# Impute missing values 
impute.res <- impute(res,method = "knn")
9 rows with more than 50 % entries missing;
 mean imputation used for these rows
Cluster size 12068 broken into 11832 236 
Cluster size 11832 broken into 8982 2850 
Cluster size 8982 broken into 3383 5599 
Cluster size 3383 broken into 1164 2219 
Done cluster 1164 
Cluster size 2219 broken into 1271 948 
Done cluster 1271 
Done cluster 948 
Done cluster 2219 
Done cluster 3383 
Cluster size 5599 broken into 3042 2557 
Cluster size 3042 broken into 1360 1682 
Done cluster 1360 
Cluster size 1682 broken into 631 1051 
Done cluster 631 
Done cluster 1051 
Done cluster 1682 
Done cluster 3042 
Cluster size 2557 broken into 1453 1104 
Done cluster 1453 
Done cluster 1104 
Done cluster 2557 
Done cluster 5599 
Done cluster 8982 
Cluster size 2850 broken into 725 2125 
Done cluster 725 
Cluster size 2125 broken into 1238 887 
Done cluster 1238 
Done cluster 887 
Done cluster 2125 
Done cluster 2850 
Done cluster 11832 
Done cluster 236 
pData(impute.res)$Sample = gsub('\\s+','',pData(impute.res)$Sample)
# Plot imputed values 
res.miss = melt(exprs(res))
colnames(res.miss) = c("Row","Dosage","Abundance_imp")
res.no.miss = melt(exprs(impute.res))
colnames(res.no.miss) = c("Row","Dosage","Abundance")
imp.vals = res.no.miss[which(is.na(res.miss$Abundance_imp)),]
# Some boxplots of the data
#---------------------------
# All data including missing values
boxplot(log2(res.miss$Abundance)~as.factor(res.miss$Dosage),las=2,col=rep(c("turquoise", "salmon","palegreen","plum1"),times=c(3,3,3,1)),main="All data including missing values")

# Imputed values/missing values only
b.imp = boxplot(log2(imp.vals$Abundance)~imp.vals$Dosage,las=2,col=rep(c("turquoise", "salmon","palegreen","plum1"),times=c(3,3,3,1)),main="Imputed values only")

boxplot(log2(imp.vals$Abundance)~imp.vals$Dosage,las=2,names = paste(b.imp$names," (n=", b.imp$n, ")",sep=""),col=rep(c("turquoise", "salmon","palegreen","plum1"),times=c(3,3,3,1)),main="Imputed values only",,cex.axis = 0.6)

# All data icluding newly imputed values
boxplot(log2(res.no.miss$Abundance)~as.factor(res.no.miss$Dosage),las=2,col=rep(c("turquoise", "salmon","palegreen","plum1"),times=c(3,3,3,1)),main="All data with imputed values")

# Additional plots showing fraction of missing values
# Not much use as in our case, it is very small. 
vim.dat = data.frame(cbind(Abundance_imp=res.miss$Abundance_imp,Abundance=res.no.miss$Abundance),stringsAsFactors = F)
head(vim.dat)
vim.dat$which.miss = FALSE
vim.dat$which.miss[which(is.na(vim.dat$Abundance_imp))] = TRUE
table(vim.dat$which.miss)

 FALSE   TRUE 
119977    793 
histMiss(vim.dat, only.miss = F)

Click in in the left margin to switch to the previous variable or in the right margin to switch to the next variable.
To regain use of the VIM GUI and the R console, click anywhere else in the graphics window.

pbox(vim.dat, ylim=c(0,500),selection="none")

Click in in the left margin to switch to the previous variable or in the right margin to switch to the next variable.
To regain use of the VIM GUI and the R console, click anywhere else in the graphics window.

matrixplot(vim.dat)

Click in a column to sort by the corresponding variable.
To regain use of the VIM GUI and the R console, click outside the plot region.

We have a small number of missing values - 793 peptides have one or more missing values out of 12244 peptides in total. 551/793 are missing in 1 sample only and 38/793 in 2 samples. Only 2 peptides are missing in 9/10 samples.

# ---------------------------------------------------------------------------------
# Step 03 : Normalising imputed data using various methods to determine ideal one
# ---------------------------------------------------------------------------------
qnt.max <- normalise(impute.res, "max")
qnt.sum <- normalise(impute.res, "sum")
qnt.quant <- normalise(impute.res, "quantiles")
qnt.qrob <- normalise(impute.res, "quantiles.robust")
qnt.vsn <- normalise(impute.res, "vsn")
vsn2: 12077 x 10 matrix (1 stratum). Please use 'meanSdPlot' to verify the fit.
## ---- plotting function---------
.plot <- function(x,ttl=NULL) {
  boxplot(exprs(x),
          main=ifelse(is.null(ttl),processingData(x)@processing[2],ttl),
          cex.main=1.5,
          cex.lab=.5,
          cex.axis=0.8,
          cex=.8,
          las=2)
  grid()
}
# Using the plotting function to plot boxplots for all diff types of normalisation methods
oldmar <- par()$mar
par(mfrow=c(3,2),mar=c(2.9,2.9,2.9,1))
.plot(impute.res, ttl = "Non-normalised data")
.plot(qnt.max, ttl = "Maximum")
.plot(qnt.sum, ttl = "Sum")
.plot(qnt.quant, ttl = "Quantile")
.plot(qnt.qrob, ttl = "Robust quantile")
.plot(qnt.vsn, ttl = "vsn")

# Checking the effects of normalisation on covariance
sd1 <- apply(log2(exprs(impute.res))+10,1,sd)
mn1 <- apply(log2(exprs(impute.res))+10,1,mean)
cv1 <- sd1/mn1
sd2 <- apply(exprs(qnt.vsn)+10,1,sd)
mn2 <- apply(exprs(qnt.vsn)+10,1,mean)
cv2 <- sd2/mn2
dfr <- rbind(data.frame(rank=order(mn1),cv=cv1,norm="raw"),
             data.frame(rank=order(mn2),cv=cv2,norm="vsn"))
rmed1 <- rollapply(cv1,7,function(x) median(x,na.rm=TRUE))
rmed2 <- rollapply(cv2,7,function(x) median(x,na.rm=TRUE))
dfr2 <- rbind(data.frame(x=seq(0,30,by=30/length(rmed1))[-1],y=rmed1,norm="raw"),data.frame(x=seq(0,30,by=30/length(rmed2))[-1],y=rmed2,norm="vsn"))
p <- ggplot()+geom_line(data=dfr2,aes(x=x,y=y,col=norm)) + theme_gray(7)
plot(p)

Will keep the data from the vsn normalisation for downstream analyses as it normalises the data better than other methods used in this comparison such as “sum”, “max”, “quantile”.

For all further steps, we will use the object “qnt.vsn”

# ---------------------------------------------------------------------------------
# Step 04a : Aggregate peptide data to protein expression values
# There is an in-built function called 'combineFeatures' to do thi within MSnBase
# ---------------------------------------------------------------------------------
protnames <- fData(qnt.vsn)$Master.Protein.Accessions
#table(protnames)
length(unique(protnames)) # 1744 proteins present in all 10 samples
[1] 1744
# Aggregating peptide abundance values into protein abundance values
qnt.prot <- combineFeatures(qnt.vsn, groupBy = protnames, fun = "median")
Combined 12077 features into 12077 using median
dim(qnt.prot)
[1] 1744   10
head(exprs(qnt.prot))
            150mJ.1 150mJ.2  150mJ.3   275mJ.1  275mJ.2  275mJ.3  400mJ.1  400mJ.2  400mJ.3   Pool.1
A0A024QZP7 3.816467 3.620175 3.927225 4.052191 3.357771 3.310799 3.357685 3.268323 3.294870 3.651255
A0A024R216 7.599068 8.060989 6.553413 5.027294 7.777270 7.677502 7.354644 7.367304 7.238476 7.079061
A0A024R4E5 6.329437 5.803502 6.730733 6.286203 6.320219 6.172760 5.830347 5.964010 5.750300 6.287400
A0A024R644 5.396222 5.055941 4.600265 4.333401 4.729008 4.757164 4.156337 4.452842 4.393996 4.887391
A0A075B716 6.043305 5.677170 5.677677 5.247188 5.606109 5.426848 5.970442 5.801846 5.696659 5.619219
A0A087WSV8 4.562525 5.022626 4.362861 4.670520 4.962158 5.500905 6.268788 5.923221 5.913728 4.863713
head(exprs(qnt.vsn))
   150mJ.1 150mJ.2  150mJ.3   275mJ.1  275mJ.2  275mJ.3  400mJ.1  400mJ.2  400mJ.3   Pool.1
2 2.768493 1.966696 6.596663 6.509413 2.401781 6.540405 1.903026 6.520467 6.507783 6.511016
3 4.166554 4.035286 5.620443 6.395213 4.589417 5.245986 6.144995 5.496511 5.716597 5.117277
4 5.200775 5.765512 5.219895 5.058563 5.142504 5.314060 4.817990 4.729333 4.899958 4.730216
5 5.892083 5.608025 5.671977 5.279402 5.237583 4.981204 5.499543 5.309686 5.272549 5.135761
6 5.455946 5.232610 4.995591 5.433890 5.079733 4.986121 5.175707 5.402227 5.270593 5.470324
7 2.355212 1.760496 4.276767 3.345973 1.786217 1.852261 2.003715 2.348329 1.819560 2.025044
# Basic plots of protein data across samples
.plot(qnt.prot,ttl="Aggregated-proteins")

plot(hclust(dist(exprs(t(qnt.prot)))))

# Looking at sample correlations
cor.prot = cor(exprs(qnt.prot))
heatmap(cor.prot,cex.main = 0.8)

dissimilarity <- 1 - cor.prot
distance <- as.dist(dissimilarity)
plot(hclust(distance))

pairs(x = exprs(qnt.prot), upper.panel=NULL, pch=20)

# Using the "duplicatecorrelation" function in limma to test correlation between technical replicates
# If Rayner's ordering is correct, then the samples are
biolrep.rq <- c(1,1,1,2,2,2,3,3,3,4)
# If there is a swap between Pool.1 and 275mj.rep1, then it should be
biolrep.swap <- c(1,1,1,4,2,2,3,3,3,2)
# If it is indeed a swap, then the correlation should increase when corrected. It does!
rq.cor = duplicateCorrelation(qnt.prot,ndups=1,block=biolrep.rq)$consensus.correlation # 0.203
swap.cor = duplicateCorrelation(qnt.prot,ndups=1,block=biolrep.swap)$consensus.correlation # 0.564
rq.cor
[1] 0.2031868
swap.cor
[1] 0.5641893
cor.prot
           150mJ.1  150mJ.2   150mJ.3    275mJ.1   275mJ.2   275mJ.3   400mJ.1   400mJ.2   400mJ.3    Pool.1
150mJ.1  1.0000000 0.9335052 0.7911591 0.4153498 0.9280369 0.8853188 0.6821358 0.7285274 0.6684451 0.8458091
150mJ.2  0.9335052 1.0000000 0.7551314 0.4370340 0.9500877 0.9236256 0.7780666 0.8027901 0.7780055 0.8816818
150mJ.3  0.7911591 0.7551314 1.0000000 0.5989892 0.7830856 0.7672976 0.6600698 0.6912729 0.6334646 0.7386629
275mJ.1  0.4153498 0.4370340 0.5989892 1.0000000 0.5515125 0.5783829 0.6087879 0.6728370 0.6634102 0.6850855
275mJ.2  0.9280369 0.9500877 0.7830856 0.5515125 1.0000000 0.9663206 0.8269695 0.8625385 0.8276158 0.9248869
275mJ.3  0.8853188 0.9236256 0.7672976 0.5783829 0.9663206 1.0000000 0.8494414 0.8778792 0.8443968 0.9432591
400mJ.1  0.6821358 0.7780666 0.6600698 0.6087879 0.8269695 0.8494414 1.0000000 0.9486792 0.9224763 0.8623312
400mJ.2  0.7285274 0.8027901 0.6912729 0.6728370 0.8625385 0.8778792 0.9486792 1.0000000 0.9264799 0.9100994
400mJ.3  0.6684451 0.7780055 0.6334646 0.6634102 0.8276158 0.8443968 0.9224763 0.9264799 1.0000000 0.8713971
Pool.1   0.8458091 0.8816818 0.7386629 0.6850855 0.9248869 0.9432591 0.8623312 0.9100994 0.8713971 1.0000000
#duplicateCorrelation(qnt.prot[,1:9],ndups=1,block=c(1,1,1,2,2,2,3,3,3))$consensus.correlation # 0.23
#duplicateCorrelation(qnt.prot[,c(1:3,5:10)],ndups=1,block=c(1,1,1,2,2,3,3,3,2))$consensus.correlation # 0.37

We have merged the peptides into proteins and are looking here at the correlations within replicates of UV dosage. Replicate 3 of 150mJ dosage is a bit off from the other two while replicate 1 of 275mJ sits by itself allowing the “Pool” sample to cluster with the other 275mJ samples.

I thought that 275mJ.3 and Pool might have been swapped. So the various plots were done to prove whether or not this was true. The “duplicateCorrelation” function yields a much higher correlation when we assume that 275mJ.3 is swapped with Pool.1 than if we didn’t.

Looking at th correlation values, 275.mJ vs Pool is more correlated than 150mJ vs Pool and 400mJ vs Pool. This could be becuase there was more material from sample 275mJ going into the pool. Rayner used the same volume of each of the 9 samples rather than same amount in the pool. Hence the higher correlation (well at least it is my best guess).

# ---------------------------------------------------------------------------------
# Step 04a : Plotting PCAs 
# This is to look at variability across samples and within replicates
# ---------------------------------------------------------------------------------
# Across all 10 samples
prot.pca = prcomp(t(exprs(qnt.prot)),scale=T)
j <- ggbiplot(prot.pca, var.axes=F, groups = factor(c(1,1,1,2,2,2,3,3,3,4)), circle = T,obs.scale=1,labels=rownames(prot.pca$x))
print(j)

# Excluding the Pool.1 sample
prot.pca.rq = prcomp(t(exprs(qnt.prot[,c(1:9)])),scale=T)
g <- ggbiplot(prot.pca.rq, var.axes=F, groups = factor(c(1,1,1,2,2,2,3,3,3)), circle = T,obs.scale=1,labels=rownames(prot.pca.rq$x))
g <- g+labs(colour = "UV Dosage")
print(g)

# Assuming Pool.1 is actually 275mJ.rep1
prot.pca.swap = prcomp(t(exprs(qnt.prot[,c(1:3,5:10)])),scale=T)
h <- ggbiplot(prot.pca.swap, var.axes=F, groups = factor(c(1,1,1,2,2,3,3,3,2)), circle = T,obs.scale=1,labels=rownames(prot.pca.swap$x))
print(h)

# Removing both problem samples
prot.pca.no.dud = prcomp(t(exprs(qnt.prot[,c(1:2,5:10)])),scale=T)
k <- ggbiplot(prot.pca.no.dud, var.axes=F, groups = factor(c(1,1,2,2,3,3,3,4)), circle = T,obs.scale=1,labels=rownames(prot.pca.no.dud$x))
print(k)

# To finish off, some boxplots....
pData(qnt.prot) = pData(impute.res)
# Are the values vastly different
boxplot(exprs(qnt.prot),las=2)

I’ve been looking to explain what I see. It seems like the most obvious answer would be that Pool.1 and 275mJ.1 are swapped. However, upon speaking with Rayner, this did not happen. He sets up the tubes in a row and adds the labels in order so the last sample was the pool and the 4th was 275mJ.1. What Rayner did say was that the tube with 150mJ.3 was dropped and sample had a little shake/tumble which might have affected its quality. I don’t know at which stage of the protocol this happened but it did. This would explain the separation of 150mJ.3 from the other two 150mJs. However, the 150mJ triplicate still cluster together on the hclust plots. With sample 275mJ.1, Rayner remembers it being odd but we don’t know why or how.

An alternate explanation is that sample 275mJ.1 did not elute(?) out properly while it was being prepared but it did by the time the pool was made. The pool was made of equal volumes of all 9 samples rather than equal protein amounts so there will be a higher representation of more abundant samples and lower of less abundant samples.

Have gone back to look at the “cor.prot” matrix and as Tom suspected, the Pool.1 correlates slightly better with non-dodgey 275mJ (corr = 0.94) than with 400mJ (0.88) and non-dodgey 150mJ (0.87) but not significantly more, hence the cluster plot looks like it does. The two dogey samples 150mJ.3 and 275mJ.1 have been left out of the average correlation calculation above.

Would like to take a picture of the table of final concentrations of samples that went into the pool and put it in the pData dataframe so we have it for furture reference. Rayner is re-doing experiment.

The boxplots seem to indicate that the two dodgey samples are missing proteins that are lowly expressed. Maybe they were too low to be detected by the mass spec ?

# -------------------------------------------------------------------------------------------------------------------------------------
# Step 05: Creating a background list of proteins for U20S to be used for functional enrichment
# Using GO terms and Interpro domains for function of proteins detected by UV crosslinking and Trizol-enrichment
# All data are based on Trizol-4-step and 400mJ of UV exposure for 2 mins (?)
# -------------------------------------------------------------------------------------------------------------------------------------
#==========================================================================================================================================
# Using U2OS protein list from Geiger et al as the background list. This consists of maximal list of proteins expressed in U20S cell lines
# ~ 7500 proteins. We are however reading in the peptides and aggregating them using MSnbase as we did with the UV dosage data
#==========================================================================================================================================
# Read data from Tom's list of Geiger proteins
# Tom took the Supplementary data from paper and re-annotated it with master proteins using his script. 
# The script gets focuses on using Swissprot IDs rather than both Swissprot and Trembl. 
# It also marks which proteins are "crap" or "contaminants"
# Finally, it provides a measure of which peptides could be mapped to a unique protein. We use this as a filter for downstream analyses
u2os <- read.delim("Input/Geiger_et_al_2012_U2OS_peptides_plus_master.txt",sep="\t",header=T)
head(u2os)
dim(u2os) # 68621 21
[1] 68621    21
# Filter data - keep peptides with unique master proteins, those which are not "crap" and those that are not missing a master protein
u2os.filt1 = u2os[which(u2os$unique == 1 & u2os$master_protein != "" & u2os$crap_protein != 1),]
# Keep only those columnns with data of interest
# Geiger et al produced a triplicate MS dataset for the U2OS cell line
u2os.filt2 = u2os.filt1[,c(5:6,17,11:16,18:19)]
head(u2os.filt2)
# Checking for peptide loss. We loose ~ 400 peptides out of nearly 70,000 so not too concerned
dim(u2os) # 68621 21
[1] 68621    21
dim(u2os.filt1) # 64967 21
[1] 64967    21
dim(u2os.filt2) # 64967 11
[1] 64967    11
# Create an MSnSet object of the background list 
u2os.samp = data.frame(sample=c("Intensity.U2OS_1","Intensity.U2OS_2","Intensity.U2OS_3"),rep=c(1,2,3))
rownames(u2os.samp) = u2os.samp$sample
res.u2os <- MSnSet(exprs = as.matrix(u2os.filt2[,c(5:7)]),fData=u2os.filt2[,-c(5:7)],pData = u2os.samp)
# Impute missing data in the background list from Geiger et al.
# Haven't drawn any plots for this. Can do it at a later date
impute.u2os <- impute(res.u2os,method = "knn")
28533 rows with more than 50 % entries missing;
 mean imputation used for these rows
Cluster size 36434 broken into 28537 7897 
Cluster size 28537 broken into 7812 20725 
Cluster size 7812 broken into 4633 3179 
Cluster size 4633 broken into 3045 1588 
Cluster size 3045 broken into 1231 1814 
Done cluster 1231 
Cluster size 1814 broken into 860 954 
Done cluster 860 
Done cluster 954 
Done cluster 1814 
Done cluster 3045 
Cluster size 1588 broken into 758 830 
Done cluster 758 
Done cluster 830 
Done cluster 1588 
Done cluster 4633 
Cluster size 3179 broken into 1044 2135 
Done cluster 1044 
Cluster size 2135 broken into 1220 915 
Done cluster 1220 
Done cluster 915 
Done cluster 2135 
Done cluster 3179 
Done cluster 7812 
Cluster size 20725 broken into 18245 2480 
Cluster size 18245 broken into 9841 8404 
Cluster size 9841 broken into 3640 6201 
Cluster size 3640 broken into 1877 1763 
Cluster size 1877 broken into 788 1089 
Done cluster 788 
Done cluster 1089 
Done cluster 1877 
Cluster size 1763 broken into 775 988 
Done cluster 775 
Done cluster 988 
Done cluster 1763 
Done cluster 3640 
Cluster size 6201 broken into 3330 2871 
Cluster size 3330 broken into 2482 848 
Cluster size 2482 broken into 1250 1232 
Done cluster 1250 
Done cluster 1232 
Done cluster 2482 
Done cluster 848 
Done cluster 3330 
Cluster size 2871 broken into 1561 1310 
Cluster size 1561 broken into 350 1211 
Done cluster 350 
Done cluster 1211 
Done cluster 1561 
Done cluster 1310 
Done cluster 2871 
Done cluster 6201 
Done cluster 9841 
Cluster size 8404 broken into 5706 2698 
Cluster size 5706 broken into 2228 3478 
Cluster size 2228 broken into 1013 1215 
Done cluster 1013 
Done cluster 1215 
Done cluster 2228 
Cluster size 3478 broken into 1269 2209 
Done cluster 1269 
Cluster size 2209 broken into 1207 1002 
Done cluster 1207 
Done cluster 1002 
Done cluster 2209 
Done cluster 3478 
Done cluster 5706 
Cluster size 2698 broken into 1450 1248 
Done cluster 1450 
Done cluster 1248 
Done cluster 2698 
Done cluster 8404 
Done cluster 18245 
Cluster size 2480 broken into 1298 1182 
Done cluster 1298 
Done cluster 1182 
Done cluster 2480 
Done cluster 20725 
Done cluster 28537 
Cluster size 7897 broken into 4366 3531 
Cluster size 4366 broken into 1923 2443 
Cluster size 1923 broken into 1388 535 
Done cluster 1388 
Done cluster 535 
Done cluster 1923 
Cluster size 2443 broken into 1369 1074 
Done cluster 1369 
Done cluster 1074 
Done cluster 2443 
Done cluster 4366 
Cluster size 3531 broken into 1164 2367 
Done cluster 1164 
Cluster size 2367 broken into 1331 1036 
Done cluster 1331 
Done cluster 1036 
Done cluster 2367 
Done cluster 3531 
Done cluster 7897 
# Aggregate Geiger et al data from peptides to proteins
# 64967 peptides are aggregated to 7507 proteins
agg.u2os = combineFeatures(impute.u2os,groupBy = fData(impute.u2os)$master_protein,cv = T,fun = "median")
Combined 64967 features into 64967 using median
dim(agg.u2os)
[1] 7507    3

There are 7507 proteins in the U20S cell line, across 3 replicates, based on the Geiger et al., dataset. The next step is to annotate this gene list with the various functional categories that we want to perform enrichment analysis for.

#------------------------------------------------------------------------------------------------------------------------
# Step 06: Mapping 'background' as well as 'expressed' list of proteins to various annotations
# Used a few different packages - wanted to settle on getBM from 'biomaRt' as output in easy to use format. 
# Except that it was extremely slow. Hence used 'queryMany' from mygene and will reformat data.
#------------------------------------------------------------------------------------------------------------------------
#------------------------------------------------------
# Mapping Geiger U2OS list of proteins to GO terms
#------------------------------------------------------
# Geiger list being annotated with interpro descriptions - fast as done in chunks
# 7507 uniprot IDs yield 7574 lines of data
library(mygene)
Loading required package: GenomicFeatures
Loading required package: GenomeInfoDb
Loading required package: GenomicRanges

Attaching package: ‘mygene’

The following object is masked from ‘package:biomaRt’:

    getGene
geiger.qm = queryMany(fData(agg.u2os)$master_protein,scopes="uniprot",fields=c("ensembl","name","symbol","interpro","go"))
Querying chunk 1
Querying chunk 2
Querying chunk 3
Querying chunk 4
Querying chunk 5
Querying chunk 6
Querying chunk 7
Querying chunk 8
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.
geiger.qm$domains = sapply(sapply(geiger.qm$interpro,"[[",3),function(x) paste(x,collapse="; "))
geiger.qm$go.bp = sapply(sapply(geiger.qm$go.BP,"[[",2),function(x) paste(x,collapse="; "))
geiger.qm$go.mf = sapply(sapply(geiger.qm$go.MF,"[[",2),function(x) paste(x,collapse="; "))
geiger.qm$go.cc = sapply(sapply(geiger.qm$go.CC,"[[",2),function(x) paste(x,collapse="; "))
geiger.qm$go.all = paste(geiger.qm$go.bp,geiger.qm$go.cc,geiger.qm$go.mf,sep="; ")
#head(geiger.qm)
#-------------------------------------------------------------------------
# Mapping Trizol-enriched list of proteins to gene domains from Interpro.
#-------------------------------------------------------------------------
# 1744 uniprot IDs yield 1780 lines of data
uv.qm = queryMany(fData(qnt.prot)$Master.Protein.Accessions,scopes="uniprot",fields=c("ensembl","name","symbol","interpro","go"))
Querying chunk 1
Querying chunk 2
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.
uv.qm$domains = sapply(sapply(uv.qm$interpro,"[[",3),function(x) paste(x,collapse="; "))
uv.qm$go.bp = sapply(sapply(uv.qm$go.BP,"[[",2),function(x) paste(x,collapse="; "))
uv.qm$go.mf = sapply(sapply(uv.qm$go.MF,"[[",2),function(x) paste(x,collapse="; "))
uv.qm$go.cc = sapply(sapply(uv.qm$go.CC,"[[",2),function(x) paste(x,collapse="; "))
uv.qm$go.all = paste(uv.qm$go.bp,uv.qm$go.cc,uv.qm$go.mf,sep="; ")
#head(uv.qm)

Now that we have mapped genes to annotations, time for some enrichment analysis.

#------------------------------------------------------------------------------------------------------------------------
# Step 07: Performing enrichment analysis using 'goseq' package
# We use GO terms and Interpro domains for enrichment analysis
#------------------------------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------
# Function  : runGoseq
# Aim       : runs goseq on a list of genes
# Input     : list of genes
# Output    : enriched list of Interpro domains
# ------------------------------------------------------------------------
runGoseq <- function(genelist,bglist,bias.dat=NULL,cat.oligo){
  # setting up goseq object
  all.genes.comp = rep(0,nrow(bglist))
  names(all.genes.comp) = rownames(bglist)
  all.genes.comp[which(names(all.genes.comp) %in% unique(genelist))] = 1
  table(all.genes.comp)
  
  # Remove missing values
  comp.no.missing = all.genes.comp[which(!is.na(names(all.genes.comp)))]
  table(comp.no.missing)
  
    
  # Running the function to calculate weights. We have no bias information as we did in UV experiment
  # This is because mass spec was run in detection mode not quantitation mode. 
  pwf.comp = nullp(comp.no.missing,'hg19','geneSymbol', bias=bias.dat,plot.fit=TRUE)
  
  # goseq enrichment with domains for cross-linked samples
  goseq.comp = goseq(pwf.comp,gene2cat = cat.oligo)
  goseq.comp$BH_over_represented_pvalue = p.adjust(goseq.comp$over_represented_pvalue,method = "BH")
  goseq.comp
  
  enriched.goseq.comp = goseq.comp[which(goseq.comp$BH_over_represented_pvalue <= 0.05),]
  return(list(goseq.comp,enriched.goseq.comp))
}
#----------------------------------------------------
# Using Goseq with protein abundance as a bias....
#----------------------------------------------------
# Want to be able to use both UniProt and Gene symbols as references for bias in downstream analysis
bias.df = data.frame(protbias = rowMax(exprs(agg.u2os)),UNIPROT = fData(agg.u2os)$master_protein,SYMBOL=sapply(strsplit(as.character(fData(agg.u2os)$protein_description),"\\||\\_"),"[[",3))
head(bias.df)
# Converting Geiger et al domains data to a list with each line only having one domain
geiger.doms = data.frame(geiger.qm[,c("query","domains")])
geiger.gos = data.frame(geiger.qm[,c("query","go.all")])
library(data.table)
d.dt <- data.table(geiger.doms, key="query")
geiger.cat <- d.dt[, list(domains = unlist(strsplit(domains, "; "))), by=query]
go.dt = data.table(geiger.gos, key="query")
geiger.cat.go <- go.dt[, list(go.terms = unlist(strsplit(go.all, "; "))), by=query]
# Setting up a 'genes' vector. We've only kept those genes present in u2os as the universe
# Then we mark all those that are enriched in the analysis as genes of interest (DE if you will)
# We have 1435 genes out of 1516 that have bias data and go terms
# Running goseq with GO categories
rownames(bias.df) = bias.df$UNIPROT
uv.enrich.go = runGoseq(uv.qm$query,bias.df,bias.df$protbias,geiger.cat.go)
initial point very close to some inequality constraints

# uv.enrich.go = uv.enrich.go[order(uv.enrich.go$BH_over_represented_pvalue),]
# Running goseq with Interpro domains
uv.enrich.interpro = runGoseq(uv.qm$query,bias.df,bias.df$protbias,geiger.cat)
initial point very close to some inequality constraints

# Writing output to text files
write.table(data.frame(uv.enrich.go[[2]]),paste(outdir,"Trizol-UV-dosage_GO-enrichment.txt",sep="/"),sep="\t",row.names=F,quote=F)
write.table(data.frame(uv.enrich.interpro[[2]]),paste(outdir,"Trizol-UV-dosage_Interpro-enrichment.txt",sep="/"),sep="\t",row.names=F,quote=F)

The protein “universe” used here is the list of all proteins discovered in Geiger et al., 2012. The list of peptides was mapped to proteins by Tom and annotated with master protein identifiers, crap proteins and uniqueness. This was used as it is the most comprehensive list of U2OS proteins mapped using mass spec to date. After filtering, there were ~7500 proteins generated in the study.

The “DE list” or “enriched” list of proteins are those that were expressed in the UV dosage experiment across all 9 samples. This yields 1744 proteins of which ~1500 could be mapped to GO identifiers. I tried doing this mapping with both ‘bioMart’ and ‘MyGene’ databases available as packages within R. The latter was phenomenally faster, hence I proceeded with it.

For GO enrichment, I used the ‘goseq’ package which accounts for any bias (here abundance of protein) and then checks for enrichment. ‘goseq’ yielded 94 terms of which 40 were BP, 36 were CC and 18 were MF terms. Of the BP (Biological Process) terms, more than half were involved in RNA processing and translation.

Would like to map each of the proteins to PFAM/SMART domains to see if they are indeed RNA binding proteins…… Have mapped RNA BP domains to both Geiger and UV.dosage. Need to look at the genes involved for which we have further evidence that they are indeed RBPs

The domain “Nucleotide-bd_a/b_plait” is present in almost all (16/18) heterogeneous nuclear ribonucleoproteins whose main task is to move mRNA out of the nucleus. This domain has an interpro entry IPR012677 which is no longer valid. This domain is also present in nucleolin and EWSR1. This might be the “RGG-box” domain eluded to in Burd and Dreyfuss, 1994. Excitingly, “Nucleotide-bd_a/b_plait” is a top domain the goseq analysis. I will substitute hnRNP searches with this term.

“Ig-like”/“Ig_sub” from Tom’s notes are indicative of glycoproteins which might be RNA-binding. There are 58 Ig term related proteins in the UV-dosage experiments of which only 2 have RNA-binding domains indicating only a small fraction have RNA binding capapbilities but majority of them are involved in other functions.

length(cl.and.nc.prot) # 163 present in both crosslinked and non-crosslinked samples
[1] 163

In this first step, we are reading in oligodT data from one experiment with both crosslinked(cl) and non-crosslinked(nc) samples. We remove proteins from the ‘cl’ which were also present in ‘nc’ as we cannot comment on enrichment.

Interestingly, the oligodT data seems a lot “cleaner” than trizol dataset. By this I mean, the top domains are most definitely all known RNA-binding domains based on literature looking at RBPs (Lunde 2007, Burd 1994). In the Trizol data we get “Ig-like” domains as one of the top hits which we don’t see at all in the oligodT data.

Between crosslinked and non-crosslinked samples, we still see some overlap in that we are getting RNA-binding domains and proteins in non-crosslinked samples but the number of such proteins in a LOT lower in non-crosslinked than in crosslinked samples.

obj.names = c("oligo.cl.in.nc","oligo.cl","oligo.nc","cl.and.nc.prot")
count = 0
for(y in list(oligo.cl.in.nc,oligo.cl,oligo.nc,cl.and.nc.prot)){
  count = count+1
  y = queryMany(y,scopes="uniprot",fields=c("ensembl","name","symbol","interpro","go"))
  y$domains = sapply(sapply(y$interpro,"[[",3),function(x) paste(x,collapse="; "))
  y$num.rbds = rowSums(sapply(rbd.doms, function(x) grepl(x, y$domains)))
  y$which.rbd = apply(sapply(rbd.doms, function(x) grepl(x, y$domains)),1, function(z) paste(names(which(z==T)),collapse="; "))
  print(table(y$num.rbds))
  print(paste("Percentage of proteins with RNA-binding domains in ", obj.names[count]," = ",round(100*sum(table(y$num.rbds)[2:4])/sum(table(y$num.rbds)),2),sep=""))
}
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.

  0   1   2   3 
449 107  99  13 
[1] "Percentage of proteins with RNA-binding domains in oligo.cl.in.nc = 32.78"
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.

  0   1   2   3 
338  83  63  10 
[1] "Percentage of proteins with RNA-binding domains in oligo.cl = 31.58"
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.

  0   1   2   3 
119  27  37   3 
[1] "Percentage of proteins with RNA-binding domains in oligo.nc = 36.02"
Finished
Pass returnall=TRUE to return lists of duplicate or missing query terms.

  0   1   2   3 
111  24  36   3 
[1] "Percentage of proteins with RNA-binding domains in cl.and.nc.prot = 36.21"
#-----------------------------------------------------------------------
# 10 : Looking at the intersect of proteins between oligodT and Trizol
#-----------------------------------------------------------------------

# Using Trizol mapped to hgnc_symbol as oligodT is only in symbols.
library(venn)
Trizol = unique(uv.qm$query)
oligodT = unique(oligo.cl)
v = venn(list(Trizol=Trizol,oligodT=oligodT),intersections=T)

both = attr(v,"intersection")$`Trizol:oligodT` # n = 298
oligo.only = attr(v,"intersection")$`oligodT` # n = 191
trizol.only = attr(v,"intersection")$`Trizol` # n = 1446

# Enrichment for overlaps and setdiffs
m = c("both","oligo-only","trizol-only")
c = 0
for(t in list(both=both,oligo.only=oligo.only,trizol.only=trizol.only)){
  c=c+1
  t.enrich.go = runGoseq(t,bias.df, bias.df$protbias,geiger.cat.go)
  t.enrich.interpro = runGoseq(t,bias.df, bias.df$protbias,geiger.cat)
  write.table(t.enrich.go[[2]][,c(1,6:7,4:5,2,8)],paste(outdir,paste(m[c],"-genes-GO-enrichment.txt",sep=""),sep="/"),sep="\t",row.names=F,quote=F)
  write.table(t.enrich.interpro[[2]],paste(outdir, paste(m[c],"-genes-Interpro-enrichment.txt",sep=""),sep="/"),sep="\t",row.names=F,quote=F)
}

oo = intersect(oligo.only,geiger.qm$query) # 170/191
tt = intersect(trizol.only,geiger.qm$query) # 1144/1446
bl = intersect(both,geiger.qm$query) # 297/298 mising EIF3C/Q99613
#------------------------------------------------------------------------------------------------------------------------
# Step 09 : Performing enrichment analysis using 'clusterProfiler' and 'goseq' packages
# In the first instance we will use GO terms and KEGG terms for enrichment analysis
#------------------------------------------------------------------------------------------------------------------------
geiger = as.character(unique(fData(agg.u2os)$master_protein))
beck = read.table("Input/Beck2011_n5781.txt")
beck = as.character(beck$V1)
lundberg.ens = read.table("Input/Lundberg2010_n5480.txt")
lundberg = unique(bitr(as.character(lundberg.ens$V1),fromType="ENSEMBL", toType="UNIPROT", OrgDb="org.Hs.eg.db")$UNIPROT)
'select()' returned 1:many mapping between keys and columns
2.81% of input gene IDs are fail to map...
length(lundberg)
[1] 9892
library(venn)

Attaching package: ‘venn’

The following object is masked from ‘package:gplots’:

    venn
library(gplots)
library(limma)
venn.u2os = venn(list(Geiger2012=geiger,Beck2011=beck,Lundberg2010=lundberg))

Small exercise on how much Geiger et al, Beck et al, and Lundberg et al., protein sets from Mass Spectrometery experiments overlap. With Lundberg et al., I had to map Ensembl IDs to UNIPROT and then do the comparison which caused a one-to-many mapping. The excess 4941 only found in Lundberg et al is almost exclusively due to the mapping of one Ensembl ID to many UNIPROT IDs. Geiger et al, being the most recent study does claim to have maximal protein mapping for U2OS. Beck et al.,

Should we use just the 4145 that overlaps as our high confident set or focus on Geiger as it the most recent ?

LS0tCnRpdGxlOiAiQW5hbHlzaW5nIHRoZSBmaXJzdCBzZXQgb2YgVE1ULXRhZ2dlZCBkYXRhIGZvciBUcml6b2wtYmFzZWQgVVYgZG9zYWdlIGV4cGVyaW1lbnRzIgpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpgYGB7ciBnbG9iYWxfb3B0aW9ucywgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTgsd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSkKI3RpZHkub3B0cz1saXN0KHdpZHRoLmN1dG9mZj04MCkKYGBgCgpgYGB7ciBBX1N0YXJ0dXAsIGhpZGU9VCx3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgQXV0aG9yIAkgICAgICA6IE1hbmFzYSBSYW1ha3Jpc2huYSwgbXIzMjVAbGUuYWMudWsKIyBEYXRlIHN0YXJ0ZWQgCTogNHRoIEF1Z3VzdCwgMjAxNwojIExhc3QgbW9kaWZpZWQgOiA0dGggQXVndXN0LCAyMDE3CiMgQWltIAkJICAgICAgOiBUbyB0YWtlIGEgbG9vayBhdCBmaXJzdCBTSUxBQyBsYWJlbGxlZCBMT1BJVCBkYXRhIG9uIFRyaXpvbAojIERlcGVuZHMgICAgICAgOiBPbiAnc2lsYWNGdW5jdGlvbnMuUicuIE1ha2Ugc3VyZSB0aGV5IGFyZSBpbiB0aGUgc2FtZSBkaXJlY3RvcnkKIyBOb3RlcyAgICAgICAgIDogV29ya3Mgb24gZGF0YSBmcm9tIFJheW5lcidzIGZpcnN0IGV4cGVyaW1lbnRzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gCgoKIyBJbnZva2luZyBsaWJyYXJpZXMKbGlicmFyeShNU25iYXNlKQpsaWJyYXJ5KGdwbG90cykKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShWSU0pCmxpYnJhcnkoem9vKSAKbGlicmFyeShzcGF0c3RhdCkgIyAiaW0iIGZ1bmN0aW9uIApsaWJyYXJ5KGdnYmlwbG90KQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkoImJpb21hUnQiKQpsaWJyYXJ5KGdvc2VxKQpsaWJyYXJ5KGxpbW1hKQpsaWJyYXJ5KGdncGxvdDIpCgpsaWJyYXJ5KG91dGxpZXJzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShzdHJpbmdyKQoKI1NldHRpbmcgd29ya2luZyBkaXJlY3Rvcmllcwp3ZCA9ICIvVXNlcnMvbWFuYXNhL0RvY3VtZW50cy9Xb3JrL1RUVC9UaHJlZVRzL3Byb3Rlb21pY3Mvb2xpZ29kVC8iCnNldHdkKHdkKQpnZXR3ZCgpCgppbmRpciA9IHBhc3RlKHdkLCJJbnB1dCIsc2VwPSIvIikKb3V0ZGlyID0gcGFzdGUod2QscGFzdGUoU3lzLkRhdGUoKSwiT3V0cHV0IixzZXAgPSAiXyIpLHNlcCA9ICIvIikKCmlmIChleGlzdHMob3V0ZGlyKSl7CiAgcHJpbnQoIk91dGRpciBleGlzdHMiKQp9ZWxzZXsKICBkaXIuY3JlYXRlKG91dGRpcikKfQoKYGBgCk5vdyB0aGF0IHdlIGhhdmUgbG9hZGVkIGFsbCB0aGUgcGFja2FnZXMgd2UgbmVlZCBmb3Igd29ya2luZyB3aXRoIHRoaXMgZGF0YSwgbGV0J3MgbW92ZSBvbiB0byB0aGUgZGF0YS4gCgpgYGB7ciAwMF9SZWFkaW5nRGF0YX0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDAwOiBSZWFkIGRhdGEKIyBSZWFkIGluIGFsbCB0aGUgZGF0YSByZXF1aXJlZCBmb3IgYW5hbHlzaXMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBGaWxlIG9mIGNvbnRhbWluYW50cyAtIHByb3RlaW5zIHRvIGV4Y2x1ZGUgZnJvbSBhbmFseXNpcyBhcyBhcmUgdGhpbmdzIGxpa2Uga2VyYXRpbiwgYWxjb2hvbCBkZWh5ZHJvZ2VuYXNlIGV0Yy4uLi4KY29udGFtID0gcmVhZC5kZWxpbSgiSW5wdXQvQ29tbW9uIGNvbnRhbWluYW50X2FsbC5jc3YiLHNlcD0iLCIsaGVhZGVyPVQpCgojIFJlYWQgaW4gdGhlIHNhbXBsZSBmaWxlIHRoYXQgbWF0Y2hlcyBjb2x1bW5zIHRvIHNhbXBsZSBjb250ZW50cwpzYW1wLmRhdCA9IHJlYWQuZGVsaW0oIklucHV0L3NhbXBsZXMudHh0IixzZXA9Ilx0IixoZWFkZXI9VCkKCiMgUmVhZCBpbiB0aGUgZGF0YSBmaWxlcyB0aGF0IGNvbnRhaW4gcGVwdGlkZSBsZXZlbCBvdXRwdXQgZnJvbSBQcm90ZW9tZSBkaXNjb3ZlcmVyLi4uCiMgTm90ZTogVGhlIGNvbHVtbnMgdGhhdCBiZWdpbiB3aXRoICJGb3VuZC5pbi5TYW1wbGUuaW4iIGNvcnJlc3BvbmQgdG8gdmFyaW91cyBzYW1wbGVzIGluIHRoZSBzdHVkeS4KIyBDb2x1bW5zIG9mIGludGVyZXN0IGFyZSAic2VxdWVuY2UiLCAibW9kaWZpY2F0aW9ucyIsIm1hc3Rlci5wcm90ZWluLmFjY2Vzc2lvbnMiLCJhYnVuZGFuY2UiLCJxdWFuLmluZm8iCmRhdGEgPSByZWFkLmRlbGltKCJJbnB1dC9Eb3NhZ2VzXzRzdGVwLVRyaXpvbF9QZXB0aWRlR3JvdXBzLnR4dCIsc2VwPSJcdCIsY29tbWVudC5jaGFyPSIiLGFzLmlzPVQsaGVhZGVyPVQpCgojIFN1YnNldCBkYXRhIHRvIG9ubHkga2VlcCBjb2x1bW5zIG9mIGludGVyZXN0CnByb3QuZGF0YSA9IGRhdGFbLGMoMTo2LDEwOjE0LDE3LDQyOjUxLDYyKV0KCiMgUmVuYW1lIHRtdCB0YWdnZWQgY29sdW1ucyB3aXRoIFVWIGRvc2FnZSBuYW1lcwpmb3IoaSBpbiAxOm5yb3coc2FtcC5kYXQpKXsKICBpZCA9IGdyZXAoc2FtcC5kYXQkVE1UW2ldLGNvbG5hbWVzKHByb3QuZGF0YSkpCiAgY29sbmFtZXMocHJvdC5kYXRhKVtpZF0gPSBwYXN0ZShzYW1wLmRhdCRTYW1wbGVbaV0pCn0KZGltKHByb3QuZGF0YSkKaGVhZChwcm90LmRhdGEpCmBgYApkYXRhIGhhcyAyMyBjb2x1bW5zIGFuZCAxNDQ1NiByb3dzIC0gZWFjaCByb3cgYmVsb25naW5nIHRvIGEgcGVwdGlkZSBhYnVuZGFuY2UgdmFsdWUgYWNyb3NzIGFsbCAxMCBzYW1wbGVzLiBXZSBub3cgZ28gdGhyb3VnaCBhIHNlcmllcyBvZiBmaWx0ZXJpbmcgc3RlcHMgdG8gb2J0YWluIGEgZGF0YXNldCB3ZSBjYW4gdXNlIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzLiAKCgpgYGB7ciAwMV9GaWx0ZXJpbmdfMX0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgU3RlcCAwMSA6IEZpbHRlciAKIyBXZSBwZXJmb3JtIDMgbGF5ZXJzIG9mIGZpbHRlcmluZyAtIHVuaXF1ZSBwcm90ZWlucywgY29udGFtaW5hbnRzLG1pc3NpbmcgdmFsdWVzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFN0ZXAgMWEgOiBGaWx0ZXIgb25seSBmb3IgdGhvc2UgcGVwdGlkZXMgdGhhdCBoYXZlIGEgdW5pcXVlIG1hc3RlciBwcm90ZWluLiBEb25lIHVzaW5nIGNvbHVtbiAicXVhbi5pbmZvIiBhbmQgdGl0bGVkICdVbmlxdWUnCnBlcHRpZGUuc3RhdHMgPSB0YWJsZShwcm90LmRhdGEkUXVhbi5JbmZvKQpwZXB0aWRlLnN0YXRzCgpmaWx0LjFhID0gcHJvdC5kYXRhW3doaWNoKHByb3QuZGF0YSRRdWFuLkluZm8gPT0gIlVuaXF1ZSIpLF0KZGltKGZpbHQuMWEpICMxMjMwMSBhcmUgdW5pcXVlIHBlcHRpZGVzLCA3MTUgYXJlIG5vbi11bmlxdWUgYW5kIDE0NDAgYXJlIG1pc3NpbmcgdmFsdWVzIAoKIyBTdGVwIDFiIDogRmlsdGVyIG91dCB0aG9zZSBwcm90ZWlucyB0aGF0IGFyZSBjb250YW1pbmFudHMgZnJvbSB0aGUgY29udGFtaW5hbnRzIGxpc3QgYW5kIGFubm90YXRlIG1pc3NpbmcgdmFsdWVzCmZpbHQuMWIgPSBmaWx0LjFhWy13aGljaChmaWx0LjFhJE1hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnMgJWluJSBjb250YW0kUHJvdGVpbi5Hcm91cC5BY2Nlc3Npb25zKSxdCm51bS5jb250YW1zID0gbGVuZ3RoKHdoaWNoKGZpbHQuMWEkTWFzdGVyLlByb3RlaW4uQWNjZXNzaW9ucyAlaW4lIGNvbnRhbSRQcm90ZWluLkdyb3VwLkFjY2Vzc2lvbnMpKQoKZGltKGZpbHQuMWEpICMgMTIzMDEgaW4gdG90YWwKZGltKGZpbHQuMWIpICMgMTIwNzcgZmlsdGVyZWQgcHJvdGVpbnMKcHJpbnQobnVtLmNvbnRhbXMpICMgMjI0IGNvbnRhbWluYW50IHByb3RlaW5zCgojIEFkZGluZyBleHRyYSBpbmZvcm1hdGlvbiBhYm91dCByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMKZmlsdC4xYiRjb3VudC5taXNzaW5nID0gcm93U3Vtcyhpcy5uYShmaWx0LjFiWyxjKDEzOjIyKV0pKQoKZmlsdC4xYiRNaXNzaW5nID0gRkFMU0UKZmlsdC4xYiRNaXNzaW5nW3doaWNoKGZpbHQuMWIkY291bnQubWlzc2luZyA+IDApXSA9IFRSVUUKCmhlYWQoZmlsdC4xYikKCmBgYApXZSBoYXZlIGEgY29sdW1uIGNhbGxlZCAiTWlzc2luZyIgdG8gaWRlbnRpZnkgd2hpY2ggcGVwdGlkZXMgaGF2ZSBvbmUgb3IgbW9yZSBtaXNzaW5nIHZhbHVlcyBhY3Jvc3MgdGhlIDEwIHNhbXBsZXMuICJjb3VudC5taXNzaW5nIiB0ZWxscyB1cyBob3cgbWFueSBtaXNzaW5nIHZhbHVlcyB0aGVyZSBhcmUgZm9yIHRoYXQgcGVwdGlkZS4KCgpgYGB7ciAwMmFfQ3JlYXRpbmctYW4tTVNuU2V0fQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDAyYSA6IENyZWF0aW5nIGFuIE1TblNldCB3aGljaCBpcyBuZWVkZWQgZm9yIHVzaW5nIHRoZSBNU25iYXNlIGJhY2thZ2UKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVGhlIHJvd25hbWVzIG9mIHNhbXAuZGF0IGhhdmUgdG8gYmUgdGhlIHNhbWUgYXMgY29sdW1uIG5hbWVzIGluIHRoZSBleHByZXNzaW9uIGRhdGEgbWF0cml4CnJvd25hbWVzKHNhbXAuZGF0KSA9IHNhbXAuZGF0JFNhbXBsZQoKIyBDcmVhdGUgYW4gTVNuU2V0IG9iamVjdApyZXMgPC0gTVNuU2V0KGV4cHJzID0gYXMubWF0cml4KGZpbHQuMWJbLGMoMTM6MjIpXSksZkRhdGE9ZmlsdC4xYlssYygxMCwxOjksMTIsMjM6MjUpXSxwRGF0YSA9IHNhbXAuZGF0WyxjKDIsMSldKQpyZXMgPC0gcmVzW3Jvd1N1bXMoaXMubmEoZXhwcnMocmVzKSkpIT0xMCxdICMgZXhjbHVkZSAgcGVwdGlkZXMgd2l0aG91dCBhbnkgcXVhbnRpZmljYXRpb24KcHJpbnQocmVzKQoKIyBIb3cgbWFueSBtaXNzaW5nIHZhbHVlcyBwZXIgcGVwdGlkZQp0YWJsZShmRGF0YShyZXMpJGNvdW50Lm1pc3NpbmcpCmNvbFN1bXMoaXMubmEoZXhwcnMocmVzKSkpCnRhYmxlKHJvd1N1bXMoaXMubmEoZXhwcnMocmVzKSkpKQoKIyBDaGVja2luZyBtaXNzaW5nIHZhbHVlcwp0YWJsZShpcy5uYShyZXMpKQpgYGAKVGhlIE1TblNldCBvYmplY3QgaGFzIGJlZW4gY3JlYXRlZCB0byBpbmNsdWRlIHByb3RlaW4gYWJ1bmRhbmNlIHZhbHVlcywgc29tZSBtZXRhZGF0YSBhbmQgc2FtcGxlIGluZm9ybWF0aW9uIChVViBkb3NhZ2UgaW4gdGhpcyBjYXNlKQoKYGBge3IgMDJiX0ltcHV0aW5nLW1pc3NpbmctdmFsdWVzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDAyYiA6IEltcHV0aW5nIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBwcm90ZWluIGV4cHJlc3Npb24gZGF0YSB1c2luZyAnaW1wdXRlJwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdWJzZXR0aW5nIG9ubHkgdGhvc2UgcGVwdGlkZXMgd2l0aCBvbmUgb3IgbW9yZSBtaXNzaW5nIHZhbHVlcwojIFJlcGxhY2luZyBtaXNzaW5nIHZhbHVlcyB3aXRoIDAgYW5kIG5vbi1taXNzaW5nIHdpdGggMQojIERpc3BsYXlpbmcgbWlzc2luZyB2YWx1ZXMKCm1pc3MubWFueSA9IHJlc1tyb3dTdW1zKGlzLm5hKGV4cHJzKHJlcykpKT49MSxdCmV4cHJzKG1pc3MubWFueSlbZXhwcnMobWlzcy5tYW55KSAhPSAwXSA9IDEKZXhwcnMobWlzcy5tYW55KVtpcy5uYShleHBycyhtaXNzLm1hbnkpKV0gPSAwCgpoZWF0bWFwLjIoZXhwcnMobWlzcy5tYW55KSwgY29sID0gYygibGlnaHRncmF5IiwgImJsYWNrIiksCiAgICAgICAgICAgIHNjYWxlID0gIm5vbmUiLCBkZW5kcm9ncmFtID0gIm5vbmUiLAogICAgICAgICAgICB0cmFjZSA9ICJub25lIiwga2V5c2l6ZSA9IDAuNSwga2V5ID0gRkFMU0UsQ29sdj1GLAogICAgICAgICAgICBDb2xTaWRlQ29sb3JzID0gcmVwKGMoInN0ZWVsYmx1ZSIsICJkYXJrb2xpdmVncmVlbiIsIm1hZ2VudGEiLCJibGFjayIpLCB0aW1lcyA9IGMoMywzLDMsMSkpKQoKIyBJbXB1dGUgbWlzc2luZyB2YWx1ZXMgCmltcHV0ZS5yZXMgPC0gaW1wdXRlKHJlcyxtZXRob2QgPSAia25uIikKcERhdGEoaW1wdXRlLnJlcykkU2FtcGxlID0gZ3N1YignXFxzKycsJycscERhdGEoaW1wdXRlLnJlcykkU2FtcGxlKQoKIyBQbG90IGltcHV0ZWQgdmFsdWVzIApyZXMubWlzcyA9IG1lbHQoZXhwcnMocmVzKSkKY29sbmFtZXMocmVzLm1pc3MpID0gYygiUm93IiwiRG9zYWdlIiwiQWJ1bmRhbmNlX2ltcCIpCnJlcy5uby5taXNzID0gbWVsdChleHBycyhpbXB1dGUucmVzKSkKY29sbmFtZXMocmVzLm5vLm1pc3MpID0gYygiUm93IiwiRG9zYWdlIiwiQWJ1bmRhbmNlIikKCmltcC52YWxzID0gcmVzLm5vLm1pc3Nbd2hpY2goaXMubmEocmVzLm1pc3MkQWJ1bmRhbmNlX2ltcCkpLF0KCiMgU29tZSBib3hwbG90cyBvZiB0aGUgZGF0YQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEFsbCBkYXRhIGluY2x1ZGluZyBtaXNzaW5nIHZhbHVlcwpib3hwbG90KGxvZzIocmVzLm1pc3MkQWJ1bmRhbmNlKX5hcy5mYWN0b3IocmVzLm1pc3MkRG9zYWdlKSxsYXM9Mixjb2w9cmVwKGMoInR1cnF1b2lzZSIsICJzYWxtb24iLCJwYWxlZ3JlZW4iLCJwbHVtMSIpLHRpbWVzPWMoMywzLDMsMSkpLG1haW49IkFsbCBkYXRhIGluY2x1ZGluZyBtaXNzaW5nIHZhbHVlcyIpCgojIEltcHV0ZWQgdmFsdWVzL21pc3NpbmcgdmFsdWVzIG9ubHkKYi5pbXAgPSBib3hwbG90KGxvZzIoaW1wLnZhbHMkQWJ1bmRhbmNlKX5pbXAudmFscyREb3NhZ2UsbGFzPTIsY29sPXJlcChjKCJ0dXJxdW9pc2UiLCAic2FsbW9uIiwicGFsZWdyZWVuIiwicGx1bTEiKSx0aW1lcz1jKDMsMywzLDEpKSxtYWluPSJJbXB1dGVkIHZhbHVlcyBvbmx5IikKYm94cGxvdChsb2cyKGltcC52YWxzJEFidW5kYW5jZSl+aW1wLnZhbHMkRG9zYWdlLGxhcz0yLG5hbWVzID0gcGFzdGUoYi5pbXAkbmFtZXMsIiAobj0iLCBiLmltcCRuLCAiKSIsc2VwPSIiKSxjb2w9cmVwKGMoInR1cnF1b2lzZSIsICJzYWxtb24iLCJwYWxlZ3JlZW4iLCJwbHVtMSIpLHRpbWVzPWMoMywzLDMsMSkpLG1haW49IkltcHV0ZWQgdmFsdWVzIG9ubHkiLCxjZXguYXhpcyA9IDAuNikKCiMgQWxsIGRhdGEgaWNsdWRpbmcgbmV3bHkgaW1wdXRlZCB2YWx1ZXMKYm94cGxvdChsb2cyKHJlcy5uby5taXNzJEFidW5kYW5jZSl+YXMuZmFjdG9yKHJlcy5uby5taXNzJERvc2FnZSksbGFzPTIsY29sPXJlcChjKCJ0dXJxdW9pc2UiLCAic2FsbW9uIiwicGFsZWdyZWVuIiwicGx1bTEiKSx0aW1lcz1jKDMsMywzLDEpKSxtYWluPSJBbGwgZGF0YSB3aXRoIGltcHV0ZWQgdmFsdWVzIikKCiMgQWRkaXRpb25hbCBwbG90cyBzaG93aW5nIGZyYWN0aW9uIG9mIG1pc3NpbmcgdmFsdWVzCiMgTm90IG11Y2ggdXNlIGFzIGluIG91ciBjYXNlLCBpdCBpcyB2ZXJ5IHNtYWxsLiAKCnZpbS5kYXQgPSBkYXRhLmZyYW1lKGNiaW5kKEFidW5kYW5jZV9pbXA9cmVzLm1pc3MkQWJ1bmRhbmNlX2ltcCxBYnVuZGFuY2U9cmVzLm5vLm1pc3MkQWJ1bmRhbmNlKSxzdHJpbmdzQXNGYWN0b3JzID0gRikKCmhlYWQodmltLmRhdCkKdmltLmRhdCR3aGljaC5taXNzID0gRkFMU0UKdmltLmRhdCR3aGljaC5taXNzW3doaWNoKGlzLm5hKHZpbS5kYXQkQWJ1bmRhbmNlX2ltcCkpXSA9IFRSVUUKdGFibGUodmltLmRhdCR3aGljaC5taXNzKQoKaGlzdE1pc3ModmltLmRhdCwgb25seS5taXNzID0gRikKcGJveCh2aW0uZGF0LCB5bGltPWMoMCw1MDApLHNlbGVjdGlvbj0ibm9uZSIpCm1hdHJpeHBsb3QodmltLmRhdCkKCmBgYApXZSBoYXZlIGEgc21hbGwgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIC0gNzkzIHBlcHRpZGVzIGhhdmUgb25lIG9yIG1vcmUgbWlzc2luZyB2YWx1ZXMgb3V0IG9mIDEyMjQ0IHBlcHRpZGVzIGluIHRvdGFsLiA1NTEvNzkzIGFyZSBtaXNzaW5nIGluIDEgc2FtcGxlIG9ubHkgYW5kIDM4Lzc5MyBpbiAyIHNhbXBsZXMuIE9ubHkgMiBwZXB0aWRlcyBhcmUgbWlzc2luZyBpbiA5LzEwIHNhbXBsZXMuCgpgYGB7ciAwM19Ob3JtYWxpc2F0aW9ufQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDAzIDogTm9ybWFsaXNpbmcgaW1wdXRlZCBkYXRhIHVzaW5nIHZhcmlvdXMgbWV0aG9kcyB0byBkZXRlcm1pbmUgaWRlYWwgb25lCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpxbnQubWF4IDwtIG5vcm1hbGlzZShpbXB1dGUucmVzLCAibWF4IikKcW50LnN1bSA8LSBub3JtYWxpc2UoaW1wdXRlLnJlcywgInN1bSIpCnFudC5xdWFudCA8LSBub3JtYWxpc2UoaW1wdXRlLnJlcywgInF1YW50aWxlcyIpCnFudC5xcm9iIDwtIG5vcm1hbGlzZShpbXB1dGUucmVzLCAicXVhbnRpbGVzLnJvYnVzdCIpCnFudC52c24gPC0gbm9ybWFsaXNlKGltcHV0ZS5yZXMsICJ2c24iKQoKIyMgLS0tLSBwbG90dGluZyBmdW5jdGlvbi0tLS0tLS0tLQoucGxvdCA8LSBmdW5jdGlvbih4LHR0bD1OVUxMKSB7CiAgYm94cGxvdChleHBycyh4KSwKICAgICAgICAgIG1haW49aWZlbHNlKGlzLm51bGwodHRsKSxwcm9jZXNzaW5nRGF0YSh4KUBwcm9jZXNzaW5nWzJdLHR0bCksCiAgICAgICAgICBjZXgubWFpbj0xLjUsCiAgICAgICAgICBjZXgubGFiPS41LAogICAgICAgICAgY2V4LmF4aXM9MC44LAogICAgICAgICAgY2V4PS44LAogICAgICAgICAgbGFzPTIpCiAgZ3JpZCgpCn0KCiMgVXNpbmcgdGhlIHBsb3R0aW5nIGZ1bmN0aW9uIHRvIHBsb3QgYm94cGxvdHMgZm9yIGFsbCBkaWZmIHR5cGVzIG9mIG5vcm1hbGlzYXRpb24gbWV0aG9kcwpvbGRtYXIgPC0gcGFyKCkkbWFyCnBhcihtZnJvdz1jKDMsMiksbWFyPWMoMi45LDIuOSwyLjksMSkpCi5wbG90KGltcHV0ZS5yZXMsIHR0bCA9ICJOb24tbm9ybWFsaXNlZCBkYXRhIikKLnBsb3QocW50Lm1heCwgdHRsID0gIk1heGltdW0iKQoucGxvdChxbnQuc3VtLCB0dGwgPSAiU3VtIikKLnBsb3QocW50LnF1YW50LCB0dGwgPSAiUXVhbnRpbGUiKQoucGxvdChxbnQucXJvYiwgdHRsID0gIlJvYnVzdCBxdWFudGlsZSIpCi5wbG90KHFudC52c24sIHR0bCA9ICJ2c24iKQoKIyBDaGVja2luZyB0aGUgZWZmZWN0cyBvZiBub3JtYWxpc2F0aW9uIG9uIGNvdmFyaWFuY2UKc2QxIDwtIGFwcGx5KGxvZzIoZXhwcnMoaW1wdXRlLnJlcykpKzEwLDEsc2QpCm1uMSA8LSBhcHBseShsb2cyKGV4cHJzKGltcHV0ZS5yZXMpKSsxMCwxLG1lYW4pCmN2MSA8LSBzZDEvbW4xCnNkMiA8LSBhcHBseShleHBycyhxbnQudnNuKSsxMCwxLHNkKQptbjIgPC0gYXBwbHkoZXhwcnMocW50LnZzbikrMTAsMSxtZWFuKQpjdjIgPC0gc2QyL21uMgpkZnIgPC0gcmJpbmQoZGF0YS5mcmFtZShyYW5rPW9yZGVyKG1uMSksY3Y9Y3YxLG5vcm09InJhdyIpLAogICAgICAgICAgICAgZGF0YS5mcmFtZShyYW5rPW9yZGVyKG1uMiksY3Y9Y3YyLG5vcm09InZzbiIpKQoKcm1lZDEgPC0gcm9sbGFwcGx5KGN2MSw3LGZ1bmN0aW9uKHgpIG1lZGlhbih4LG5hLnJtPVRSVUUpKQpybWVkMiA8LSByb2xsYXBwbHkoY3YyLDcsZnVuY3Rpb24oeCkgbWVkaWFuKHgsbmEucm09VFJVRSkpCmRmcjIgPC0gcmJpbmQoZGF0YS5mcmFtZSh4PXNlcSgwLDMwLGJ5PTMwL2xlbmd0aChybWVkMSkpWy0xXSx5PXJtZWQxLG5vcm09InJhdyIpLGRhdGEuZnJhbWUoeD1zZXEoMCwzMCxieT0zMC9sZW5ndGgocm1lZDIpKVstMV0seT1ybWVkMixub3JtPSJ2c24iKSkKCnAgPC0gZ2dwbG90KCkrZ2VvbV9saW5lKGRhdGE9ZGZyMixhZXMoeD14LHk9eSxjb2w9bm9ybSkpICsgdGhlbWVfZ3JheSg3KQpwbG90KHApCmBgYApXaWxsIGtlZXAgdGhlIGRhdGEgZnJvbSB0aGUgdnNuIG5vcm1hbGlzYXRpb24gZm9yIGRvd25zdHJlYW0gYW5hbHlzZXMgYXMgaXQgbm9ybWFsaXNlcyB0aGUgZGF0YSBiZXR0ZXIgdGhhbiBvdGhlciBtZXRob2RzIHVzZWQgaW4gdGhpcyBjb21wYXJpc29uIHN1Y2ggYXMgInN1bSIsICJtYXgiLCAicXVhbnRpbGUiLiAKCkZvciBhbGwgZnVydGhlciBzdGVwcywgd2Ugd2lsbCB1c2UgdGhlIG9iamVjdCAicW50LnZzbiIKCmBgYHtyIDA0YV9BZ2dyZWdhdGUtdG8tcHJvdGVpbnN9CgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0ZXAgMDRhIDogQWdncmVnYXRlIHBlcHRpZGUgZGF0YSB0byBwcm90ZWluIGV4cHJlc3Npb24gdmFsdWVzCiMgVGhlcmUgaXMgYW4gaW4tYnVpbHQgZnVuY3Rpb24gY2FsbGVkICdjb21iaW5lRmVhdHVyZXMnIHRvIGRvIHRoaSB3aXRoaW4gTVNuQmFzZQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKcHJvdG5hbWVzIDwtIGZEYXRhKHFudC52c24pJE1hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnMKI3RhYmxlKHByb3RuYW1lcykKbGVuZ3RoKHVuaXF1ZShwcm90bmFtZXMpKSAjIDE3NDQgcHJvdGVpbnMgcHJlc2VudCBpbiBhbGwgMTAgc2FtcGxlcwoKIyBBZ2dyZWdhdGluZyBwZXB0aWRlIGFidW5kYW5jZSB2YWx1ZXMgaW50byBwcm90ZWluIGFidW5kYW5jZSB2YWx1ZXMKcW50LnByb3QgPC0gY29tYmluZUZlYXR1cmVzKHFudC52c24sIGdyb3VwQnkgPSBwcm90bmFtZXMsIGZ1biA9ICJtZWRpYW4iKQpkaW0ocW50LnByb3QpCgpoZWFkKGV4cHJzKHFudC5wcm90KSkKaGVhZChleHBycyhxbnQudnNuKSkKCiMgQmFzaWMgcGxvdHMgb2YgcHJvdGVpbiBkYXRhIGFjcm9zcyBzYW1wbGVzCi5wbG90KHFudC5wcm90LHR0bD0iQWdncmVnYXRlZC1wcm90ZWlucyIpCnBsb3QoaGNsdXN0KGRpc3QoZXhwcnModChxbnQucHJvdCkpKSkpCgojIExvb2tpbmcgYXQgc2FtcGxlIGNvcnJlbGF0aW9ucwpjb3IucHJvdCA9IGNvcihleHBycyhxbnQucHJvdCkpCmhlYXRtYXAoY29yLnByb3QsY2V4Lm1haW4gPSAwLjgpCgpkaXNzaW1pbGFyaXR5IDwtIDEgLSBjb3IucHJvdApkaXN0YW5jZSA8LSBhcy5kaXN0KGRpc3NpbWlsYXJpdHkpCnBsb3QoaGNsdXN0KGRpc3RhbmNlKSkKCnBhaXJzKHggPSBleHBycyhxbnQucHJvdCksIHVwcGVyLnBhbmVsPU5VTEwsIHBjaD0yMCkKCiMgVXNpbmcgdGhlICJkdXBsaWNhdGVjb3JyZWxhdGlvbiIgZnVuY3Rpb24gaW4gbGltbWEgdG8gdGVzdCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRlY2huaWNhbCByZXBsaWNhdGVzCgojIElmIFJheW5lcidzIG9yZGVyaW5nIGlzIGNvcnJlY3QsIHRoZW4gdGhlIHNhbXBsZXMgYXJlCmJpb2xyZXAucnEgPC0gYygxLDEsMSwyLDIsMiwzLDMsMyw0KQoKIyBJZiB0aGVyZSBpcyBhIHN3YXAgYmV0d2VlbiBQb29sLjEgYW5kIDI3NW1qLnJlcDEsIHRoZW4gaXQgc2hvdWxkIGJlCmJpb2xyZXAuc3dhcCA8LSBjKDEsMSwxLDQsMiwyLDMsMywzLDIpCgojIElmIGl0IGlzIGluZGVlZCBhIHN3YXAsIHRoZW4gdGhlIGNvcnJlbGF0aW9uIHNob3VsZCBpbmNyZWFzZSB3aGVuIGNvcnJlY3RlZC4gSXQgZG9lcyEKcnEuY29yID0gZHVwbGljYXRlQ29ycmVsYXRpb24ocW50LnByb3QsbmR1cHM9MSxibG9jaz1iaW9scmVwLnJxKSRjb25zZW5zdXMuY29ycmVsYXRpb24gIyAwLjIwMwpzd2FwLmNvciA9IGR1cGxpY2F0ZUNvcnJlbGF0aW9uKHFudC5wcm90LG5kdXBzPTEsYmxvY2s9YmlvbHJlcC5zd2FwKSRjb25zZW5zdXMuY29ycmVsYXRpb24gIyAwLjU2NAoKcnEuY29yCnN3YXAuY29yCgpjb3IucHJvdAoKI2R1cGxpY2F0ZUNvcnJlbGF0aW9uKHFudC5wcm90WywxOjldLG5kdXBzPTEsYmxvY2s9YygxLDEsMSwyLDIsMiwzLDMsMykpJGNvbnNlbnN1cy5jb3JyZWxhdGlvbiAjIDAuMjMKI2R1cGxpY2F0ZUNvcnJlbGF0aW9uKHFudC5wcm90WyxjKDE6Myw1OjEwKV0sbmR1cHM9MSxibG9jaz1jKDEsMSwxLDIsMiwzLDMsMywyKSkkY29uc2Vuc3VzLmNvcnJlbGF0aW9uICMgMC4zNwoKYGBgCldlIGhhdmUgbWVyZ2VkIHRoZSBwZXB0aWRlcyBpbnRvIHByb3RlaW5zIGFuZCBhcmUgbG9va2luZyBoZXJlIGF0IHRoZSBjb3JyZWxhdGlvbnMgd2l0aGluIHJlcGxpY2F0ZXMgb2YgVVYgZG9zYWdlLiBSZXBsaWNhdGUgMyBvZiAxNTBtSiBkb3NhZ2UgaXMgYSBiaXQgb2ZmIGZyb20gdGhlIG90aGVyIHR3byB3aGlsZSByZXBsaWNhdGUgMSBvZiAyNzVtSiBzaXRzIGJ5IGl0c2VsZiBhbGxvd2luZyB0aGUgIlBvb2wiIHNhbXBsZSB0byBjbHVzdGVyIHdpdGggdGhlIG90aGVyIDI3NW1KIHNhbXBsZXMuIAoKSSB0aG91Z2h0IHRoYXQgMjc1bUouMyBhbmQgUG9vbCBtaWdodCBoYXZlIGJlZW4gc3dhcHBlZC4gU28gdGhlIHZhcmlvdXMgcGxvdHMgd2VyZSBkb25lIHRvIHByb3ZlIHdoZXRoZXIgb3Igbm90IHRoaXMgd2FzIHRydWUuIFRoZSAiZHVwbGljYXRlQ29ycmVsYXRpb24iIGZ1bmN0aW9uIHlpZWxkcyBhIG11Y2ggaGlnaGVyIGNvcnJlbGF0aW9uIHdoZW4gd2UgYXNzdW1lIHRoYXQgMjc1bUouMyBpcyBzd2FwcGVkIHdpdGggUG9vbC4xIHRoYW4gaWYgd2UgZGlkbid0LiAKCkxvb2tpbmcgYXQgdGggY29ycmVsYXRpb24gdmFsdWVzLCAyNzUubUogdnMgUG9vbCBpcyBtb3JlIGNvcnJlbGF0ZWQgdGhhbiAxNTBtSiB2cyBQb29sIGFuZCA0MDBtSiB2cyBQb29sLiBUaGlzIGNvdWxkIGJlIGJlY3Vhc2UgdGhlcmUgd2FzIG1vcmUgbWF0ZXJpYWwgZnJvbSBzYW1wbGUgMjc1bUogZ29pbmcgaW50byB0aGUgcG9vbC4gUmF5bmVyIHVzZWQgdGhlIHNhbWUgdm9sdW1lIG9mIGVhY2ggb2YgdGhlIDkgc2FtcGxlcyByYXRoZXIgdGhhbiBzYW1lIGFtb3VudCBpbiB0aGUgcG9vbC4gSGVuY2UgdGhlIGhpZ2hlciBjb3JyZWxhdGlvbiAod2VsbCBhdCBsZWFzdCBpdCBpcyBteSBiZXN0IGd1ZXNzKS4gCgpgYGB7ciAwNGJfUGxvdHRpbmctUENBcyB9CgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0ZXAgMDRhIDogUGxvdHRpbmcgUENBcyAKIyBUaGlzIGlzIHRvIGxvb2sgYXQgdmFyaWFiaWxpdHkgYWNyb3NzIHNhbXBsZXMgYW5kIHdpdGhpbiByZXBsaWNhdGVzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEFjcm9zcyBhbGwgMTAgc2FtcGxlcwpwcm90LnBjYSA9IHByY29tcCh0KGV4cHJzKHFudC5wcm90KSksc2NhbGU9VCkKaiA8LSBnZ2JpcGxvdChwcm90LnBjYSwgdmFyLmF4ZXM9RiwgZ3JvdXBzID0gZmFjdG9yKGMoMSwxLDEsMiwyLDIsMywzLDMsNCkpLCBjaXJjbGUgPSBULG9icy5zY2FsZT0xLGxhYmVscz1yb3duYW1lcyhwcm90LnBjYSR4KSkKcHJpbnQoaikKCiMgRXhjbHVkaW5nIHRoZSBQb29sLjEgc2FtcGxlCnByb3QucGNhLnJxID0gcHJjb21wKHQoZXhwcnMocW50LnByb3RbLGMoMTo5KV0pKSxzY2FsZT1UKQpnIDwtIGdnYmlwbG90KHByb3QucGNhLnJxLCB2YXIuYXhlcz1GLCBncm91cHMgPSBmYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpLCBjaXJjbGUgPSBULG9icy5zY2FsZT0xLGxhYmVscz1yb3duYW1lcyhwcm90LnBjYS5ycSR4KSkKZyA8LSBnK2xhYnMoY29sb3VyID0gIlVWIERvc2FnZSIpCnByaW50KGcpCgojIEFzc3VtaW5nIFBvb2wuMSBpcyBhY3R1YWxseSAyNzVtSi5yZXAxCnByb3QucGNhLnN3YXAgPSBwcmNvbXAodChleHBycyhxbnQucHJvdFssYygxOjMsNToxMCldKSksc2NhbGU9VCkKaCA8LSBnZ2JpcGxvdChwcm90LnBjYS5zd2FwLCB2YXIuYXhlcz1GLCBncm91cHMgPSBmYWN0b3IoYygxLDEsMSwyLDIsMywzLDMsMikpLCBjaXJjbGUgPSBULG9icy5zY2FsZT0xLGxhYmVscz1yb3duYW1lcyhwcm90LnBjYS5zd2FwJHgpKQpwcmludChoKQoKIyBSZW1vdmluZyBib3RoIHByb2JsZW0gc2FtcGxlcwpwcm90LnBjYS5uby5kdWQgPSBwcmNvbXAodChleHBycyhxbnQucHJvdFssYygxOjIsNToxMCldKSksc2NhbGU9VCkKayA8LSBnZ2JpcGxvdChwcm90LnBjYS5uby5kdWQsIHZhci5heGVzPUYsIGdyb3VwcyA9IGZhY3RvcihjKDEsMSwyLDIsMywzLDMsNCkpLCBjaXJjbGUgPSBULG9icy5zY2FsZT0xLGxhYmVscz1yb3duYW1lcyhwcm90LnBjYS5uby5kdWQkeCkpCnByaW50KGspCgojIFRvIGZpbmlzaCBvZmYsIHNvbWUgYm94cGxvdHMuLi4uCnBEYXRhKHFudC5wcm90KSA9IHBEYXRhKGltcHV0ZS5yZXMpCgojIEFyZSB0aGUgdmFsdWVzIHZhc3RseSBkaWZmZXJlbnQKYm94cGxvdChleHBycyhxbnQucHJvdCksbGFzPTIpCgpgYGAKSSd2ZSBiZWVuIGxvb2tpbmcgdG8gZXhwbGFpbiB3aGF0IEkgc2VlLiBJdCBzZWVtcyBsaWtlIHRoZSBtb3N0IG9idmlvdXMgYW5zd2VyIHdvdWxkIGJlIHRoYXQgUG9vbC4xIGFuZCAyNzVtSi4xIGFyZSBzd2FwcGVkLiBIb3dldmVyLCB1cG9uIHNwZWFraW5nIHdpdGggUmF5bmVyLCB0aGlzIGRpZCBub3QgaGFwcGVuLiBIZSBzZXRzIHVwIHRoZSB0dWJlcyBpbiBhIHJvdyBhbmQgYWRkcyB0aGUgbGFiZWxzIGluIG9yZGVyIHNvIHRoZSBsYXN0IHNhbXBsZSB3YXMgdGhlIHBvb2wgYW5kIHRoZSA0dGggd2FzIDI3NW1KLjEuIFdoYXQgUmF5bmVyIGRpZCBzYXkgd2FzIHRoYXQgdGhlIHR1YmUgd2l0aCAxNTBtSi4zIHdhcyBkcm9wcGVkIGFuZCBzYW1wbGUgaGFkIGEgbGl0dGxlIHNoYWtlL3R1bWJsZSB3aGljaCBtaWdodCBoYXZlIGFmZmVjdGVkIGl0cyBxdWFsaXR5LiBJIGRvbid0IGtub3cgYXQgd2hpY2ggc3RhZ2Ugb2YgdGhlIHByb3RvY29sIHRoaXMgaGFwcGVuZWQgYnV0IGl0IGRpZC4gVGhpcyB3b3VsZCBleHBsYWluIHRoZSBzZXBhcmF0aW9uIG9mIDE1MG1KLjMgZnJvbSB0aGUgb3RoZXIgdHdvIDE1MG1Kcy4gSG93ZXZlciwgdGhlIDE1MG1KIHRyaXBsaWNhdGUgc3RpbGwgY2x1c3RlciB0b2dldGhlciBvbiB0aGUgaGNsdXN0IHBsb3RzLiBXaXRoIHNhbXBsZSAyNzVtSi4xLCBSYXluZXIgcmVtZW1iZXJzIGl0IGJlaW5nIG9kZCBidXQgd2UgZG9uJ3Qga25vdyB3aHkgb3IgaG93LiAKCkFuIGFsdGVybmF0ZSBleHBsYW5hdGlvbiBpcyB0aGF0IHNhbXBsZSAyNzVtSi4xIGRpZCBub3QgZWx1dGUoPykgb3V0IHByb3Blcmx5IHdoaWxlIGl0IHdhcyBiZWluZyBwcmVwYXJlZCBidXQgaXQgZGlkIGJ5IHRoZSB0aW1lIHRoZSBwb29sIHdhcyBtYWRlLiBUaGUgcG9vbCB3YXMgbWFkZSBvZiBlcXVhbCB2b2x1bWVzIG9mIGFsbCA5IHNhbXBsZXMgcmF0aGVyIHRoYW4gZXF1YWwgcHJvdGVpbiBhbW91bnRzIHNvIHRoZXJlIHdpbGwgYmUgYSBoaWdoZXIgcmVwcmVzZW50YXRpb24gb2YgbW9yZSBhYnVuZGFudCBzYW1wbGVzIGFuZCBsb3dlciBvZiBsZXNzIGFidW5kYW50IHNhbXBsZXMuIAoKSGF2ZSBnb25lIGJhY2sgdG8gbG9vayBhdCB0aGUgImNvci5wcm90IiBtYXRyaXggYW5kIGFzIFRvbSBzdXNwZWN0ZWQsIHRoZSBQb29sLjEgY29ycmVsYXRlcyBzbGlnaHRseSBiZXR0ZXIgd2l0aCBub24tZG9kZ2V5IDI3NW1KIChjb3JyID0gMC45NCkgdGhhbiB3aXRoIDQwMG1KICgwLjg4KSBhbmQgbm9uLWRvZGdleSAxNTBtSiAoMC44NykgYnV0IG5vdCBzaWduaWZpY2FudGx5IG1vcmUsIGhlbmNlIHRoZSBjbHVzdGVyIHBsb3QgbG9va3MgbGlrZSBpdCBkb2VzLiBUaGUgdHdvIGRvZ2V5IHNhbXBsZXMgMTUwbUouMyBhbmQgMjc1bUouMSBoYXZlIGJlZW4gbGVmdCBvdXQgb2YgdGhlIGF2ZXJhZ2UgY29ycmVsYXRpb24gY2FsY3VsYXRpb24gYWJvdmUuCgpXb3VsZCBsaWtlIHRvIHRha2UgYSBwaWN0dXJlIG9mIHRoZSB0YWJsZSBvZiBmaW5hbCBjb25jZW50cmF0aW9ucyBvZiBzYW1wbGVzIHRoYXQgd2VudCBpbnRvIHRoZSBwb29sIGFuZCBwdXQgaXQgaW4gdGhlIHBEYXRhIGRhdGFmcmFtZSBzbyB3ZSBoYXZlIGl0IGZvciBmdXJ0dXJlIHJlZmVyZW5jZS4gUmF5bmVyIGlzIHJlLWRvaW5nIGV4cGVyaW1lbnQuIAoKVGhlIGJveHBsb3RzIHNlZW0gdG8gaW5kaWNhdGUgdGhhdCB0aGUgdHdvIGRvZGdleSBzYW1wbGVzIGFyZSBtaXNzaW5nIHByb3RlaW5zIHRoYXQgYXJlIGxvd2x5IGV4cHJlc3NlZC4gTWF5YmUgdGhleSB3ZXJlIHRvbyBsb3cgdG8gYmUgZGV0ZWN0ZWQgYnkgdGhlIG1hc3Mgc3BlYyA/IAoKYGBge3IgMDVfQ3JlYXRpbmctYS1iYWNrZ29ydW5kLXByb3RlaW4tbGlzdH0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0ZXAgMDU6IENyZWF0aW5nIGEgYmFja2dyb3VuZCBsaXN0IG9mIHByb3RlaW5zIGZvciBVMjBTIHRvIGJlIHVzZWQgZm9yIGZ1bmN0aW9uYWwgZW5yaWNobWVudAojIFVzaW5nIEdPIHRlcm1zIGFuZCBJbnRlcnBybyBkb21haW5zIGZvciBmdW5jdGlvbiBvZiBwcm90ZWlucyBkZXRlY3RlZCBieSBVViBjcm9zc2xpbmtpbmcgYW5kIFRyaXpvbC1lbnJpY2htZW50CiMgQWxsIGRhdGEgYXJlIGJhc2VkIG9uIFRyaXpvbC00LXN0ZXAgYW5kIDQwMG1KIG9mIFVWIGV4cG9zdXJlIGZvciAyIG1pbnMgKD8pCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBVc2luZyBVMk9TIHByb3RlaW4gbGlzdCBmcm9tIEdlaWdlciBldCBhbCBhcyB0aGUgYmFja2dyb3VuZCBsaXN0LiBUaGlzIGNvbnNpc3RzIG9mIG1heGltYWwgbGlzdCBvZiBwcm90ZWlucyBleHByZXNzZWQgaW4gVTIwUyBjZWxsIGxpbmVzCiMgfiA3NTAwIHByb3RlaW5zLiBXZSBhcmUgaG93ZXZlciByZWFkaW5nIGluIHRoZSBwZXB0aWRlcyBhbmQgYWdncmVnYXRpbmcgdGhlbSB1c2luZyBNU25iYXNlIGFzIHdlIGRpZCB3aXRoIHRoZSBVViBkb3NhZ2UgZGF0YQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgojIFJlYWQgZGF0YSBmcm9tIFRvbSdzIGxpc3Qgb2YgR2VpZ2VyIHByb3RlaW5zCiMgVG9tIHRvb2sgdGhlIFN1cHBsZW1lbnRhcnkgZGF0YSBmcm9tIHBhcGVyIGFuZCByZS1hbm5vdGF0ZWQgaXQgd2l0aCBtYXN0ZXIgcHJvdGVpbnMgdXNpbmcgaGlzIHNjcmlwdC4gCiMgVGhlIHNjcmlwdCBnZXRzIGZvY3VzZXMgb24gdXNpbmcgU3dpc3Nwcm90IElEcyByYXRoZXIgdGhhbiBib3RoIFN3aXNzcHJvdCBhbmQgVHJlbWJsLiAKIyBJdCBhbHNvIG1hcmtzIHdoaWNoIHByb3RlaW5zIGFyZSAiY3JhcCIgb3IgImNvbnRhbWluYW50cyIKIyBGaW5hbGx5LCBpdCBwcm92aWRlcyBhIG1lYXN1cmUgb2Ygd2hpY2ggcGVwdGlkZXMgY291bGQgYmUgbWFwcGVkIHRvIGEgdW5pcXVlIHByb3RlaW4uIFdlIHVzZSB0aGlzIGFzIGEgZmlsdGVyIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzCnUyb3MgPC0gcmVhZC5kZWxpbSgiSW5wdXQvR2VpZ2VyX2V0X2FsXzIwMTJfVTJPU19wZXB0aWRlc19wbHVzX21hc3Rlci50eHQiLHNlcD0iXHQiLGhlYWRlcj1UKQpoZWFkKHUyb3MpCmRpbSh1Mm9zKSAjIDY4NjIxIDIxCgojIEZpbHRlciBkYXRhIC0ga2VlcCBwZXB0aWRlcyB3aXRoIHVuaXF1ZSBtYXN0ZXIgcHJvdGVpbnMsIHRob3NlIHdoaWNoIGFyZSBub3QgImNyYXAiIGFuZCB0aG9zZSB0aGF0IGFyZSBub3QgbWlzc2luZyBhIG1hc3RlciBwcm90ZWluCnUyb3MuZmlsdDEgPSB1Mm9zW3doaWNoKHUyb3MkdW5pcXVlID09IDEgJiB1Mm9zJG1hc3Rlcl9wcm90ZWluICE9ICIiICYgdTJvcyRjcmFwX3Byb3RlaW4gIT0gMSksXQoKIyBLZWVwIG9ubHkgdGhvc2UgY29sdW1ubnMgd2l0aCBkYXRhIG9mIGludGVyZXN0CiMgR2VpZ2VyIGV0IGFsIHByb2R1Y2VkIGEgdHJpcGxpY2F0ZSBNUyBkYXRhc2V0IGZvciB0aGUgVTJPUyBjZWxsIGxpbmUKdTJvcy5maWx0MiA9IHUyb3MuZmlsdDFbLGMoNTo2LDE3LDExOjE2LDE4OjE5KV0KaGVhZCh1Mm9zLmZpbHQyKQoKIyBDaGVja2luZyBmb3IgcGVwdGlkZSBsb3NzLiBXZSBsb29zZSB+IDQwMCBwZXB0aWRlcyBvdXQgb2YgbmVhcmx5IDcwLDAwMCBzbyBub3QgdG9vIGNvbmNlcm5lZApkaW0odTJvcykgIyA2ODYyMSAyMQpkaW0odTJvcy5maWx0MSkgIyA2NDk2NyAyMQpkaW0odTJvcy5maWx0MikgIyA2NDk2NyAxMQoKIyBDcmVhdGUgYW4gTVNuU2V0IG9iamVjdCBvZiB0aGUgYmFja2dyb3VuZCBsaXN0IAp1Mm9zLnNhbXAgPSBkYXRhLmZyYW1lKHNhbXBsZT1jKCJJbnRlbnNpdHkuVTJPU18xIiwiSW50ZW5zaXR5LlUyT1NfMiIsIkludGVuc2l0eS5VMk9TXzMiKSxyZXA9YygxLDIsMykpCnJvd25hbWVzKHUyb3Muc2FtcCkgPSB1Mm9zLnNhbXAkc2FtcGxlCnJlcy51Mm9zIDwtIE1TblNldChleHBycyA9IGFzLm1hdHJpeCh1Mm9zLmZpbHQyWyxjKDU6NyldKSxmRGF0YT11Mm9zLmZpbHQyWywtYyg1OjcpXSxwRGF0YSA9IHUyb3Muc2FtcCkKCiMgSW1wdXRlIG1pc3NpbmcgZGF0YSBpbiB0aGUgYmFja2dyb3VuZCBsaXN0IGZyb20gR2VpZ2VyIGV0IGFsLgojIEhhdmVuJ3QgZHJhd24gYW55IHBsb3RzIGZvciB0aGlzLiBDYW4gZG8gaXQgYXQgYSBsYXRlciBkYXRlCmltcHV0ZS51Mm9zIDwtIGltcHV0ZShyZXMudTJvcyxtZXRob2QgPSAia25uIikKCiMgQWdncmVnYXRlIEdlaWdlciBldCBhbCBkYXRhIGZyb20gcGVwdGlkZXMgdG8gcHJvdGVpbnMKIyA2NDk2NyBwZXB0aWRlcyBhcmUgYWdncmVnYXRlZCB0byA3NTA3IHByb3RlaW5zCmFnZy51Mm9zID0gY29tYmluZUZlYXR1cmVzKGltcHV0ZS51Mm9zLGdyb3VwQnkgPSBmRGF0YShpbXB1dGUudTJvcykkbWFzdGVyX3Byb3RlaW4sY3YgPSBULGZ1biA9ICJtZWRpYW4iKQpkaW0oYWdnLnUyb3MpCmBgYApUaGVyZSBhcmUgNzUwNyBwcm90ZWlucyBpbiB0aGUgVTIwUyBjZWxsIGxpbmUsIGFjcm9zcyAzIHJlcGxpY2F0ZXMsIGJhc2VkIG9uIHRoZSBHZWlnZXIgZXQgYWwuLCBkYXRhc2V0LiBUaGUgbmV4dCBzdGVwIGlzIHRvIGFubm90YXRlIHRoaXMgZ2VuZSBsaXN0IHdpdGggdGhlIHZhcmlvdXMgZnVuY3Rpb25hbCBjYXRlZ29yaWVzIHRoYXQgd2Ugd2FudCB0byBwZXJmb3JtIGVucmljaG1lbnQgYW5hbHlzaXMgZm9yLiAKCmBgYHtyIDA2X1UyT1MtcHJvdGVpbnMtYW5ub3RhdGlvbn0KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDA2OiBNYXBwaW5nICdiYWNrZ3JvdW5kJyBhcyB3ZWxsIGFzICdleHByZXNzZWQnIGxpc3Qgb2YgcHJvdGVpbnMgdG8gdmFyaW91cyBhbm5vdGF0aW9ucwojIFVzZWQgYSBmZXcgZGlmZmVyZW50IHBhY2thZ2VzIC0gd2FudGVkIHRvIHNldHRsZSBvbiBnZXRCTSBmcm9tICdiaW9tYVJ0JyBhcyBvdXRwdXQgaW4gZWFzeSB0byB1c2UgZm9ybWF0LiAKIyBFeGNlcHQgdGhhdCBpdCB3YXMgZXh0cmVtZWx5IHNsb3cuIEhlbmNlIHVzZWQgJ3F1ZXJ5TWFueScgZnJvbSBteWdlbmUgYW5kIHdpbGwgcmVmb3JtYXQgZGF0YS4KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1hcHBpbmcgR2VpZ2VyIFUyT1MgbGlzdCBvZiBwcm90ZWlucyB0byBHTyB0ZXJtcwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEdlaWdlciBsaXN0IGJlaW5nIGFubm90YXRlZCB3aXRoIGludGVycHJvIGRlc2NyaXB0aW9ucyAtIGZhc3QgYXMgZG9uZSBpbiBjaHVua3MKIyA3NTA3IHVuaXByb3QgSURzIHlpZWxkIDc1NzQgbGluZXMgb2YgZGF0YQpsaWJyYXJ5KG15Z2VuZSkKZ2VpZ2VyLnFtID0gcXVlcnlNYW55KGZEYXRhKGFnZy51Mm9zKSRtYXN0ZXJfcHJvdGVpbixzY29wZXM9InVuaXByb3QiLGZpZWxkcz1jKCJlbnNlbWJsIiwibmFtZSIsInN5bWJvbCIsImludGVycHJvIiwiZ28iKSkKZ2VpZ2VyLnFtJGRvbWFpbnMgPSBzYXBwbHkoc2FwcGx5KGdlaWdlci5xbSRpbnRlcnBybywiW1siLDMpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCmdlaWdlci5xbSRnby5icCA9IHNhcHBseShzYXBwbHkoZ2VpZ2VyLnFtJGdvLkJQLCJbWyIsMiksZnVuY3Rpb24oeCkgcGFzdGUoeCxjb2xsYXBzZT0iOyAiKSkKZ2VpZ2VyLnFtJGdvLm1mID0gc2FwcGx5KHNhcHBseShnZWlnZXIucW0kZ28uTUYsIltbIiwyKSxmdW5jdGlvbih4KSBwYXN0ZSh4LGNvbGxhcHNlPSI7ICIpKQpnZWlnZXIucW0kZ28uY2MgPSBzYXBwbHkoc2FwcGx5KGdlaWdlci5xbSRnby5DQywiW1siLDIpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCmdlaWdlci5xbSRnby5hbGwgPSBwYXN0ZShnZWlnZXIucW0kZ28uYnAsZ2VpZ2VyLnFtJGdvLmNjLGdlaWdlci5xbSRnby5tZixzZXA9IjsgIikKI2hlYWQoZ2VpZ2VyLnFtKQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBNYXBwaW5nIFRyaXpvbC1lbnJpY2hlZCBsaXN0IG9mIHByb3RlaW5zIHRvIGdlbmUgZG9tYWlucyBmcm9tIEludGVycHJvLgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDE3NDQgdW5pcHJvdCBJRHMgeWllbGQgMTc4MCBsaW5lcyBvZiBkYXRhCnV2LnFtID0gcXVlcnlNYW55KGZEYXRhKHFudC5wcm90KSRNYXN0ZXIuUHJvdGVpbi5BY2Nlc3Npb25zLHNjb3Blcz0idW5pcHJvdCIsZmllbGRzPWMoImVuc2VtYmwiLCJuYW1lIiwic3ltYm9sIiwiaW50ZXJwcm8iLCJnbyIpKQp1di5xbSRkb21haW5zID0gc2FwcGx5KHNhcHBseSh1di5xbSRpbnRlcnBybywiW1siLDMpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCnV2LnFtJGdvLmJwID0gc2FwcGx5KHNhcHBseSh1di5xbSRnby5CUCwiW1siLDIpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCnV2LnFtJGdvLm1mID0gc2FwcGx5KHNhcHBseSh1di5xbSRnby5NRiwiW1siLDIpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCnV2LnFtJGdvLmNjID0gc2FwcGx5KHNhcHBseSh1di5xbSRnby5DQywiW1siLDIpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCnV2LnFtJGdvLmFsbCA9IHBhc3RlKHV2LnFtJGdvLmJwLHV2LnFtJGdvLmNjLHV2LnFtJGdvLm1mLHNlcD0iOyAiKQojaGVhZCh1di5xbSkKCmBgYApOb3cgdGhhdCB3ZSBoYXZlIG1hcHBlZCBnZW5lcyB0byBhbm5vdGF0aW9ucywgdGltZSBmb3Igc29tZSBlbnJpY2htZW50IGFuYWx5c2lzLiAKCmBgYHtyIDA3X1BlcmZvcm1pbmctZW5yaWNobWVudC1hbmFseXNpc30KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDA3OiBQZXJmb3JtaW5nIGVucmljaG1lbnQgYW5hbHlzaXMgdXNpbmcgJ2dvc2VxJyBwYWNrYWdlCiMgV2UgdXNlIEdPIHRlcm1zIGFuZCBJbnRlcnBybyBkb21haW5zIGZvciBlbnJpY2htZW50IGFuYWx5c2lzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgRnVuY3Rpb24gIDogcnVuR29zZXEKIyBBaW0gICAgICAgOiBydW5zIGdvc2VxIG9uIGEgbGlzdCBvZiBnZW5lcwojIElucHV0ICAgICA6IGxpc3Qgb2YgZ2VuZXMKIyBPdXRwdXQgICAgOiBlbnJpY2hlZCBsaXN0IG9mIEludGVycHJvIGRvbWFpbnMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCnJ1bkdvc2VxIDwtIGZ1bmN0aW9uKGdlbmVsaXN0LGJnbGlzdCxiaWFzLmRhdD1OVUxMLGNhdC5vbGlnbyl7CgogICMgc2V0dGluZyB1cCBnb3NlcSBvYmplY3QKICBhbGwuZ2VuZXMuY29tcCA9IHJlcCgwLG5yb3coYmdsaXN0KSkKICBuYW1lcyhhbGwuZ2VuZXMuY29tcCkgPSByb3duYW1lcyhiZ2xpc3QpCiAgYWxsLmdlbmVzLmNvbXBbd2hpY2gobmFtZXMoYWxsLmdlbmVzLmNvbXApICVpbiUgdW5pcXVlKGdlbmVsaXN0KSldID0gMQogIHRhYmxlKGFsbC5nZW5lcy5jb21wKQogIAogICMgUmVtb3ZlIG1pc3NpbmcgdmFsdWVzCiAgY29tcC5uby5taXNzaW5nID0gYWxsLmdlbmVzLmNvbXBbd2hpY2goIWlzLm5hKG5hbWVzKGFsbC5nZW5lcy5jb21wKSkpXQogIHRhYmxlKGNvbXAubm8ubWlzc2luZykKICAKICAgIAogICMgUnVubmluZyB0aGUgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHdlaWdodHMuIFdlIGhhdmUgbm8gYmlhcyBpbmZvcm1hdGlvbiBhcyB3ZSBkaWQgaW4gVVYgZXhwZXJpbWVudAogICMgVGhpcyBpcyBiZWNhdXNlIG1hc3Mgc3BlYyB3YXMgcnVuIGluIGRldGVjdGlvbiBtb2RlIG5vdCBxdWFudGl0YXRpb24gbW9kZS4gCiAgcHdmLmNvbXAgPSBudWxscChjb21wLm5vLm1pc3NpbmcsJ2hnMTknLCdnZW5lU3ltYm9sJywgYmlhcz1iaWFzLmRhdCxwbG90LmZpdD1UUlVFKQogIAogICMgZ29zZXEgZW5yaWNobWVudCB3aXRoIGRvbWFpbnMgZm9yIGNyb3NzLWxpbmtlZCBzYW1wbGVzCiAgZ29zZXEuY29tcCA9IGdvc2VxKHB3Zi5jb21wLGdlbmUyY2F0ID0gY2F0Lm9saWdvKQogIGdvc2VxLmNvbXAkQkhfb3Zlcl9yZXByZXNlbnRlZF9wdmFsdWUgPSBwLmFkanVzdChnb3NlcS5jb21wJG92ZXJfcmVwcmVzZW50ZWRfcHZhbHVlLG1ldGhvZCA9ICJCSCIpCiAgZ29zZXEuY29tcAogIAogIGVucmljaGVkLmdvc2VxLmNvbXAgPSBnb3NlcS5jb21wW3doaWNoKGdvc2VxLmNvbXAkQkhfb3Zlcl9yZXByZXNlbnRlZF9wdmFsdWUgPD0gMC4wNSksXQogIHJldHVybihsaXN0KGdvc2VxLmNvbXAsZW5yaWNoZWQuZ29zZXEuY29tcCkpCgp9CgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFVzaW5nIEdvc2VxIHdpdGggcHJvdGVpbiBhYnVuZGFuY2UgYXMgYSBiaWFzLi4uLgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBXYW50IHRvIGJlIGFibGUgdG8gdXNlIGJvdGggVW5pUHJvdCBhbmQgR2VuZSBzeW1ib2xzIGFzIHJlZmVyZW5jZXMgZm9yIGJpYXMgaW4gZG93bnN0cmVhbSBhbmFseXNpcwpiaWFzLmRmID0gZGF0YS5mcmFtZShwcm90YmlhcyA9IHJvd01heChleHBycyhhZ2cudTJvcykpLFVOSVBST1QgPSBmRGF0YShhZ2cudTJvcykkbWFzdGVyX3Byb3RlaW4sU1lNQk9MPXNhcHBseShzdHJzcGxpdChhcy5jaGFyYWN0ZXIoZkRhdGEoYWdnLnUyb3MpJHByb3RlaW5fZGVzY3JpcHRpb24pLCJcXHx8XFxfIiksIltbIiwzKSkKaGVhZChiaWFzLmRmKQoKIyBDb252ZXJ0aW5nIEdlaWdlciBldCBhbCBkb21haW5zIGRhdGEgdG8gYSBsaXN0IHdpdGggZWFjaCBsaW5lIG9ubHkgaGF2aW5nIG9uZSBkb21haW4KZ2VpZ2VyLmRvbXMgPSBkYXRhLmZyYW1lKGdlaWdlci5xbVssYygicXVlcnkiLCJkb21haW5zIildKQpnZWlnZXIuZ29zID0gZGF0YS5mcmFtZShnZWlnZXIucW1bLGMoInF1ZXJ5IiwiZ28uYWxsIildKQoKbGlicmFyeShkYXRhLnRhYmxlKQpkLmR0IDwtIGRhdGEudGFibGUoZ2VpZ2VyLmRvbXMsIGtleT0icXVlcnkiKQpnZWlnZXIuY2F0IDwtIGQuZHRbLCBsaXN0KGRvbWFpbnMgPSB1bmxpc3Qoc3Ryc3BsaXQoZG9tYWlucywgIjsgIikpKSwgYnk9cXVlcnldCgpnby5kdCA9IGRhdGEudGFibGUoZ2VpZ2VyLmdvcywga2V5PSJxdWVyeSIpCmdlaWdlci5jYXQuZ28gPC0gZ28uZHRbLCBsaXN0KGdvLnRlcm1zID0gdW5saXN0KHN0cnNwbGl0KGdvLmFsbCwgIjsgIikpKSwgYnk9cXVlcnldCgojIFNldHRpbmcgdXAgYSAnZ2VuZXMnIHZlY3Rvci4gV2UndmUgb25seSBrZXB0IHRob3NlIGdlbmVzIHByZXNlbnQgaW4gdTJvcyBhcyB0aGUgdW5pdmVyc2UKIyBUaGVuIHdlIG1hcmsgYWxsIHRob3NlIHRoYXQgYXJlIGVucmljaGVkIGluIHRoZSBhbmFseXNpcyBhcyBnZW5lcyBvZiBpbnRlcmVzdCAoREUgaWYgeW91IHdpbGwpCiMgV2UgaGF2ZSAxNDM1IGdlbmVzIG91dCBvZiAxNTE2IHRoYXQgaGF2ZSBiaWFzIGRhdGEgYW5kIGdvIHRlcm1zCgojIFJ1bm5pbmcgZ29zZXEgd2l0aCBHTyBjYXRlZ29yaWVzCnJvd25hbWVzKGJpYXMuZGYpID0gYmlhcy5kZiRVTklQUk9UCnV2LmVucmljaC5nbyA9IHJ1bkdvc2VxKHV2LnFtJHF1ZXJ5LGJpYXMuZGYsYmlhcy5kZiRwcm90YmlhcyxnZWlnZXIuY2F0LmdvKQojIHV2LmVucmljaC5nbyA9IHV2LmVucmljaC5nb1tvcmRlcih1di5lbnJpY2guZ28kQkhfb3Zlcl9yZXByZXNlbnRlZF9wdmFsdWUpLF0KCiMgUnVubmluZyBnb3NlcSB3aXRoIEludGVycHJvIGRvbWFpbnMKdXYuZW5yaWNoLmludGVycHJvID0gcnVuR29zZXEodXYucW0kcXVlcnksYmlhcy5kZixiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQpCgojIFdyaXRpbmcgb3V0cHV0IHRvIHRleHQgZmlsZXMKd3JpdGUudGFibGUoZGF0YS5mcmFtZSh1di5lbnJpY2guZ29bWzJdXSkscGFzdGUob3V0ZGlyLCJUcml6b2wtVVYtZG9zYWdlX0dPLWVucmljaG1lbnQudHh0IixzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQp3cml0ZS50YWJsZShkYXRhLmZyYW1lKHV2LmVucmljaC5pbnRlcnByb1tbMl1dKSxwYXN0ZShvdXRkaXIsIlRyaXpvbC1VVi1kb3NhZ2VfSW50ZXJwcm8tZW5yaWNobWVudC50eHQiLHNlcD0iLyIpLHNlcD0iXHQiLHJvdy5uYW1lcz1GLHF1b3RlPUYpCmBgYApUaGUgcHJvdGVpbiAidW5pdmVyc2UiIHVzZWQgaGVyZSBpcyB0aGUgbGlzdCBvZiBhbGwgcHJvdGVpbnMgZGlzY292ZXJlZCBpbiBHZWlnZXIgZXQgYWwuLCAyMDEyLiBUaGUgbGlzdCBvZiBwZXB0aWRlcyB3YXMgbWFwcGVkIHRvIHByb3RlaW5zIGJ5IFRvbSBhbmQgYW5ub3RhdGVkIHdpdGggbWFzdGVyIHByb3RlaW4gaWRlbnRpZmllcnMsIGNyYXAgcHJvdGVpbnMgYW5kIHVuaXF1ZW5lc3MuIFRoaXMgd2FzIHVzZWQgYXMgaXQgaXMgdGhlIG1vc3QgY29tcHJlaGVuc2l2ZSBsaXN0IG9mIFUyT1MgcHJvdGVpbnMgbWFwcGVkIHVzaW5nIG1hc3Mgc3BlYyB0byBkYXRlLiBBZnRlciBmaWx0ZXJpbmcsIHRoZXJlIHdlcmUgfjc1MDAgcHJvdGVpbnMgZ2VuZXJhdGVkIGluIHRoZSBzdHVkeS4gCgpUaGUgIkRFIGxpc3QiIG9yICJlbnJpY2hlZCIgbGlzdCBvZiBwcm90ZWlucyBhcmUgdGhvc2UgdGhhdCB3ZXJlIGV4cHJlc3NlZCBpbiB0aGUgVVYgZG9zYWdlIGV4cGVyaW1lbnQgYWNyb3NzIGFsbCA5IHNhbXBsZXMuIFRoaXMgeWllbGRzIDE3NDQgcHJvdGVpbnMgb2Ygd2hpY2ggfjE1MDAgY291bGQgYmUgbWFwcGVkIHRvIEdPIGlkZW50aWZpZXJzLiBJIHRyaWVkIGRvaW5nIHRoaXMgbWFwcGluZyB3aXRoIGJvdGggJ2Jpb01hcnQnIGFuZCAnTXlHZW5lJyBkYXRhYmFzZXMgYXZhaWxhYmxlIGFzIHBhY2thZ2VzIHdpdGhpbiBSLiBUaGUgbGF0dGVyIHdhcyBwaGVub21lbmFsbHkgZmFzdGVyLCBoZW5jZSBJIHByb2NlZWRlZCB3aXRoIGl0LiAKCkZvciBHTyBlbnJpY2htZW50LCBJIHVzZWQgdGhlICdnb3NlcScgcGFja2FnZSB3aGljaCBhY2NvdW50cyBmb3IgYW55IGJpYXMgKGhlcmUgYWJ1bmRhbmNlIG9mIHByb3RlaW4pIGFuZCB0aGVuIGNoZWNrcyBmb3IgZW5yaWNobWVudC4gJ2dvc2VxJyB5aWVsZGVkIDk0IHRlcm1zIG9mIHdoaWNoIDQwIHdlcmUgQlAsIDM2IHdlcmUgQ0MgYW5kIDE4IHdlcmUgTUYgdGVybXMuIE9mIHRoZSBCUCAoQmlvbG9naWNhbCBQcm9jZXNzKSB0ZXJtcywgbW9yZSB0aGFuIGhhbGYgd2VyZSBpbnZvbHZlZCBpbiBSTkEgcHJvY2Vzc2luZyBhbmQgdHJhbnNsYXRpb24uCgpXb3VsZCBsaWtlIHRvIG1hcCBlYWNoIG9mIHRoZSBwcm90ZWlucyB0byBQRkFNL1NNQVJUIGRvbWFpbnMgdG8gc2VlIGlmIHRoZXkgYXJlIGluZGVlZCBSTkEgYmluZGluZyBwcm90ZWlucy4uLi4uLgpIYXZlIG1hcHBlZCBSTkEgQlAgZG9tYWlucyB0byBib3RoIEdlaWdlciBhbmQgVVYuZG9zYWdlLiAKTmVlZCB0byBsb29rIGF0IHRoZSBnZW5lcyBpbnZvbHZlZCBmb3Igd2hpY2ggd2UgaGF2ZSBmdXJ0aGVyIGV2aWRlbmNlIHRoYXQgdGhleSBhcmUgaW5kZWVkIFJCUHMKClRoZSBkb21haW4gIk51Y2xlb3RpZGUtYmRfYS9iX3BsYWl0IiBpcyBwcmVzZW50IGluIGFsbW9zdCBhbGwgKDE2LzE4KSBoZXRlcm9nZW5lb3VzIG51Y2xlYXIgcmlib251Y2xlb3Byb3RlaW5zIHdob3NlIG1haW4gdGFzayBpcyB0byBtb3ZlIG1STkEgb3V0IG9mIHRoZSBudWNsZXVzLiBUaGlzIGRvbWFpbiBoYXMgYW4gaW50ZXJwcm8gZW50cnkgSVBSMDEyNjc3IHdoaWNoIGlzIG5vIGxvbmdlciB2YWxpZC4gVGhpcyBkb21haW4gaXMgYWxzbyBwcmVzZW50IGluIG51Y2xlb2xpbiBhbmQgRVdTUjEuIFRoaXMgbWlnaHQgYmUgdGhlICJSR0ctYm94IiBkb21haW4gZWx1ZGVkIHRvIGluIEJ1cmQgYW5kIERyZXlmdXNzLCAxOTk0LiBFeGNpdGluZ2x5LCAiTnVjbGVvdGlkZS1iZF9hL2JfcGxhaXQiIGlzIGEgdG9wIGRvbWFpbiB0aGUgZ29zZXEgYW5hbHlzaXMuIEkgd2lsbCBzdWJzdGl0dXRlIGhuUk5QIHNlYXJjaGVzIHdpdGggdGhpcyB0ZXJtLiAKCiJJZy1saWtlIi8iSWdfc3ViIiBmcm9tIFRvbSdzIG5vdGVzIGFyZSBpbmRpY2F0aXZlIG9mIGdseWNvcHJvdGVpbnMgd2hpY2ggbWlnaHQgYmUgUk5BLWJpbmRpbmcuIFRoZXJlIGFyZSA1OCBJZyB0ZXJtIHJlbGF0ZWQgcHJvdGVpbnMgaW4gdGhlIFVWLWRvc2FnZSBleHBlcmltZW50cyBvZiB3aGljaCBvbmx5IDIgaGF2ZSBSTkEtYmluZGluZyBkb21haW5zIGluZGljYXRpbmcgb25seSBhIHNtYWxsIGZyYWN0aW9uIGhhdmUgUk5BIGJpbmRpbmcgY2FwYXBiaWxpdGllcyBidXQgbWFqb3JpdHkgb2YgdGhlbSBhcmUgaW52b2x2ZWQgaW4gb3RoZXIgZnVuY3Rpb25zLgoKYGBge3IgMDhfTWFwcGluZy1vbGlnb2RULXRvLWRvbWFpbnN9CgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAwOCA6IE1hcHBpbmcgb2xpZ29kVCBkYXRhIHRvIGRvbWFpbnMgYW5kIGxvb2tpbmcgZm9yIGVucmljaG1lbnQKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpvbGlnby5jbC5pbi5uYyA9IGFzLmNoYXJhY3RlcihyZWFkUkRTKCJJbnB1dC9DTF9wcm90ZWlucy5yZHMiKSkgIyA2NTIgdW5pcXVlIFN3aXNzcHJvdCBJRHMgZnJvbSBUb20ncyBwZXB0aWRlIGFnZ3JlZ2F0ZWQgYW5kIHByb3RlaW4gcmVuYW1lZCBmaWxlCiNvbGlnby5jbC5pbi5uYyA9IHJlYWQudGFibGUoIklucHV0L0xlaWNlc3Rlci1vbGlnb2RULUNMLXByb3QudHh0IixoZWFkZXI9VCxzZXA9Ilx0IikgIyAzMjggZ2VuZSBzeW1ib2xzIGZyb20gUmF5bmVyJ3MgZmlsZQoKb2xpZ28ubmMgPSBhcy5jaGFyYWN0ZXIocmVhZFJEUygiSW5wdXQvTkNMX3Byb3RlaW5zLnJkcyIpKSAjIDE3NSB1bmlxdWUgU3dpc3Nwcm90IElEcyBmcm9tIFRvbSdzIHBlcHRpZGUgYWdncmVnYXRlZCBhbmQgcHJvdGVpbiByZW5hbWVkIGZpbGUKI29saWdvLm5jID0gcmVhZC50YWJsZSgiSW5wdXQvTGVpY2VzdGVyLW9saWdvZFQtTkMtcHJvdC50eHQiLGhlYWRlcj1ULHNlcD0iXHQiKSAjIDczIGdlbmUgc3ltYm9scyBmcm9tIFJheW5lcidzIGZpbGUKCmxlbmd0aChvbGlnby5jbC5pbi5uYykKbGVuZ3RoKG9saWdvLm5jKQoKIyBQcmVzZW50IGluIG5vbi1jcm9zc2xpbmtlZCBhbmQgaGVuY2Ugd2lsbCBiZSByZW1vdmVkCgpvbGlnby5jbCA9IG9saWdvLmNsLmluLm5jWy13aGljaChvbGlnby5jbC5pbi5uYyAlaW4lIG9saWdvLm5jKV0gCmxlbmd0aChvbGlnby5jbCkgIyA0ODkgdW5pcXVlIFN3aXNzcHJvdCBJRHMgZnJvbSBUb20ncyBmaWxlcyBub3QgcHJlc2VudCBpbiBub24tY3Jvc3NsaW5rZWQgc2FtcGxlcwoKIyBXaGljaCBnZW5lcyBhcmUgcHJlc2VudCBpbiBib3RoIGNyb3NzIGFuZCBub24tY3Jvc3NsaW5rZWQgc2FtcGxlcwpjbC5hbmQubmMucHJvdCA9IG9saWdvLmNsLmluLm5jW3doaWNoKG9saWdvLmNsLmluLm5jICVpbiUgb2xpZ28ubmMpXQpsZW5ndGgoY2wuYW5kLm5jLnByb3QpICMgMTYzIHByZXNlbnQgaW4gYm90aCBjcm9zc2xpbmtlZCBhbmQgbm9uLWNyb3NzbGlua2VkIHNhbXBsZXMKCiMgQ29udmVydCBkYXRhIHRhYmxlCiMgZ2VpZ2VyLmRvbXMuc3ltID0gZGF0YS5mcmFtZShnZWlnZXIucW1bLGMoInN5bWJvbCIsImRvbWFpbnMiKV0pCiMgZ2VpZ2VyLmdvcy5zeW0gPSBkYXRhLmZyYW1lKGdlaWdlci5xbVssYygic3ltYm9sIiwiZ28uYWxsIildKQoKIyBkLmR0LnN5bSA8LSBkYXRhLnRhYmxlKGdlaWdlci5kb21zLnN5bSwga2V5PSJzeW1ib2wiKQojIGdlaWdlci5jYXQuc3ltIDwtIGQuZHQuc3ltWywgbGlzdChkb21haW5zID0gdW5saXN0KHN0cnNwbGl0KGRvbWFpbnMsICI7ICIpKSksIGJ5PXN5bWJvbF0KCiMgZ28uZHQuc3ltID0gZGF0YS50YWJsZShnZWlnZXIuZ29zLnN5bSwga2V5PSJzeW1ib2wiKQojIGdlaWdlci5jYXQuZ28uc3ltIDwtIGdvLmR0LnN5bVssIGxpc3QoZ28udGVybXMgPSB1bmxpc3Qoc3Ryc3BsaXQoZ28uYWxsLCAiOyAiKSkpLCBieT1zeW1ib2xdCgojIFNldHRpbmcgdXAgYSAnZ2VuZXMnIHZlY3Rvci4gV2UndmUgb25seSBrZXB0IHRob3NlIGdlbmVzIHByZXNlbnQgaW4gdTJvcyBhcyB0aGUgdW5pdmVyc2UKIyBUaGVuIHdlIG1hcmsgYWxsIHRob3NlIHRoYXQgYXJlIGVucmljaGVkIGluIHRoZSBhbmFseXNpcyBhcyBnZW5lcyBvZiBpbnRlcmVzdCAoREUgaWYgeW91IHdpbGwpCiMgV2UgaGF2ZSAxNDM1IGdlbmVzIG91dCBvZiAxNTE2IHRoYXQgaGF2ZSBiaWFzIGRhdGEgYW5kIGdvIHRlcm1zCgojIFJ1bm5pbmcgZ29zZXEgZm9yIG9saWdvZFQgd2hpY2ggaGF2ZSBnZW5lIHN5bWJvbHMgYXMgaWRlbnRpZmllcnMKIyByb3duYW1lcyhiaWFzLmRmKSA9IGJpYXMuZGYkU1lNQk9MCgojIENyb3NzbGlua2VkIG9saWdvZFQgYWxsIHByb3RlaW5zCmNsLmFsbC5lbnJpY2guZ28gPSBydW5Hb3NlcShvbGlnby5jbC5pbi5uYyxiaWFzLmRmLGJpYXMuZGYkcHJvdGJpYXMsZ2VpZ2VyLmNhdC5nbykKY2wuYWxsLmVucmljaC5pbnRlcnBybyA9IHJ1bkdvc2VxKG9saWdvLmNsLmluLm5jLGJpYXMuZGYsYmlhcy5kZiRwcm90YmlhcyxnZWlnZXIuY2F0KQp3cml0ZS50YWJsZShjbC5hbGwuZW5yaWNoLmdvW1syXV1bLGMoMSw2OjcsNDo1LDIsOCldLHBhc3RlKG91dGRpciwib2xpZ29kVC1jcm9zc2xpbmtlZC13aXRoLW5jLXByb3RzX0dPLWVucmljaG1lbnQudHh0IixzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQp3cml0ZS50YWJsZShjbC5hbGwuZW5yaWNoLmludGVycHJvW1syXV0scGFzdGUob3V0ZGlyLCJvbGlnb2RULWNyb3NzbGlua2VkLXdpdGgtbmMtcHJvdHNfSW50ZXJwcm8tZW5yaWNobWVudC50eHQiLHNlcD0iLyIpLHNlcD0iXHQiLHJvdy5uYW1lcz1GLHF1b3RlPUYpCgojIENyb3NzbGlua2VkIG9saWdvZFQgbWludXMgcHJvdGVpbnMgaW4gbm9uLWNyb3NzbGluZWQgc2FtcGxlcwpjbC5lbnJpY2guZ28gPSBydW5Hb3NlcShvbGlnby5jbCxiaWFzLmRmLGJpYXMuZGYkcHJvdGJpYXMsZ2VpZ2VyLmNhdC5nbykKY2wuZW5yaWNoLmludGVycHJvID0gcnVuR29zZXEob2xpZ28uY2wsYmlhcy5kZixiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQpCndyaXRlLnRhYmxlKGNsLmVucmljaC5nb1tbMl1dWyxjKDEsNjo3LDQ6NSwyLDgpXSxwYXN0ZShvdXRkaXIsIm9saWdvZFQtY3Jvc3NsaW5rZWRfR08tZW5yaWNobWVudC50eHQiLHNlcD0iLyIpLHNlcD0iXHQiLHJvdy5uYW1lcz1GLHF1b3RlPUYpCndyaXRlLnRhYmxlKGNsLmVucmljaC5pbnRlcnByb1tbMl1dLHBhc3RlKG91dGRpciwib2xpZ29kVC1jcm9zc2xpbmtlZF9JbnRlcnByby1lbnJpY2htZW50LnR4dCIsc2VwPSIvIiksc2VwPSJcdCIscm93Lm5hbWVzPUYscXVvdGU9RikKCiMgTm9uLWNyb3NzbGlua2VkIHNhbXBsZXMKbmMuZW5yaWNoLmdvID0gcnVuR29zZXEob2xpZ28ubmMsYmlhcy5kZixiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQuZ28pCm5jLmVucmljaC5pbnRlcnBybyA9IHJ1bkdvc2VxKG9saWdvLm5jLGJpYXMuZGYsYmlhcy5kZiRwcm90YmlhcyxnZWlnZXIuY2F0KQp3cml0ZS50YWJsZShuYy5lbnJpY2guZ29bWzJdXVssYygxLDY6Nyw0OjUsMiw4KV0scGFzdGUob3V0ZGlyLCJvbGlnb2RULW5vbi1jcm9zc2xpbmtlZF9HTy1lbnJpY2htZW50LnR4dCIsc2VwPSIvIiksc2VwPSJcdCIscm93Lm5hbWVzPUYscXVvdGU9RikKd3JpdGUudGFibGUobmMuZW5yaWNoLmludGVycHJvW1syXV0scGFzdGUob3V0ZGlyLCJvbGlnb2RULW5vbi1jcm9zc2xpbmtlZF9JbnRlcnByby1lbnJpY2htZW50LnR4dCIsc2VwPSIvIiksc2VwPSJcdCIscm93Lm5hbWVzPUYscXVvdGU9RikKCiMgQ29tbW9uIHRvIGJvdGggY3Jvc3MtbGlua2VkIGFuZCBub24tY3Jvc3NsaW5rZWQgc2FtcGxlcwpuYy5jbC5lbnJpY2guZ28gPSBydW5Hb3NlcShjbC5hbmQubmMucHJvdCxiaWFzLmRmLGJpYXMuZGYkcHJvdGJpYXMsZ2VpZ2VyLmNhdC5nbykKbmMuY2wuZW5yaWNoLmludGVycHJvID0gcnVuR29zZXEoY2wuYW5kLm5jLnByb3QsYmlhcy5kZixiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQpCndyaXRlLnRhYmxlKG5jLmVucmljaC5nb1tbMl1dWyxjKDEsNjo3LDQ6NSwyLDgpXSxwYXN0ZShvdXRkaXIsIm9saWdvZFQtY2wtYW5kLW5jX0dPLWVucmljaG1lbnQudHh0IixzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQp3cml0ZS50YWJsZShuYy5lbnJpY2guaW50ZXJwcm9bWzJdXSxwYXN0ZShvdXRkaXIsIm9saWdvZFQtY2wtYW5kLW5jX0ludGVycHJvLWVucmljaG1lbnQudHh0IixzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQoKCmBgYApJbiB0aGlzIGZpcnN0IHN0ZXAsIHdlIGFyZSByZWFkaW5nIGluIG9saWdvZFQgZGF0YSBmcm9tIG9uZSBleHBlcmltZW50IHdpdGggYm90aCBjcm9zc2xpbmtlZChjbCkgYW5kIG5vbi1jcm9zc2xpbmtlZChuYykgc2FtcGxlcy4gV2UgcmVtb3ZlIHByb3RlaW5zIGZyb20gdGhlICdjbCcgd2hpY2ggd2VyZSBhbHNvIHByZXNlbnQgaW4gJ25jJyBhcyB3ZSBjYW5ub3QgY29tbWVudCBvbiBlbnJpY2htZW50LgoKSW50ZXJlc3RpbmdseSwgdGhlIG9saWdvZFQgZGF0YSBzZWVtcyBhIGxvdCAiY2xlYW5lciIgdGhhbiB0cml6b2wgZGF0YXNldC4gQnkgdGhpcyBJIG1lYW4sIHRoZSB0b3AgZG9tYWlucyBhcmUgbW9zdCBkZWZpbml0ZWx5IGFsbCBrbm93biBSTkEtYmluZGluZyBkb21haW5zIGJhc2VkIG9uIGxpdGVyYXR1cmUgbG9va2luZyBhdCBSQlBzIChMdW5kZSAyMDA3LCBCdXJkIDE5OTQpLiBJbiB0aGUgVHJpem9sIGRhdGEgd2UgZ2V0ICJJZy1saWtlIiBkb21haW5zIGFzIG9uZSBvZiB0aGUgdG9wIGhpdHMgd2hpY2ggd2UgZG9uJ3Qgc2VlIGF0IGFsbCBpbiB0aGUgb2xpZ29kVCBkYXRhLgoKQmV0d2VlbiBjcm9zc2xpbmtlZCBhbmQgbm9uLWNyb3NzbGlua2VkIHNhbXBsZXMsIHdlIHN0aWxsIHNlZSBzb21lIG92ZXJsYXAgaW4gdGhhdCB3ZSBhcmUgZ2V0dGluZyBSTkEtYmluZGluZyBkb21haW5zIGFuZCBwcm90ZWlucyBpbiBub24tY3Jvc3NsaW5rZWQgc2FtcGxlcyBidXQgdGhlIG51bWJlciBvZiBzdWNoIHByb3RlaW5zIGluIGEgTE9UIGxvd2VyIGluIG5vbi1jcm9zc2xpbmtlZCB0aGFuIGluIGNyb3NzbGlua2VkIHNhbXBsZXMuIAoKYGBge3IgMDlfQW5ub3RhdGluZy1SQkQtZG9tYWluc30KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFN0ZXAgMDkgOiBBbm5vdGF0aW5nIGFsbCBwcm90ZWluIGxpc3RzIHdpdGggY291bnRzIG9mIFJCRCBkb21haW5zCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1ha2luZyBhIGxpc3Qgb2YgZG9tYWlucyB0aGF0IGRlZmluZSBSTkEgYmluZGluZyBwcm90ZWlucwojIENvdWxkbid0IGZpbmQgUElXSS9QQVogZG9tYWlucyBpbiB0aGUgVVYgZ2VuZSBsaXN0IGJ1dCBwcmVzZW50IGluIEdlaWdlciBsaXN0CiMgTG9va2luZyBhdCBCdXJkIHBhcGVyIHdoaWNoIGluY2x1ZGVzICdSR0cnIGJveCB3aGljaCBJIHRoaW5rIGlzIEhuUk5QIGFzIHdlbGwgYXMgY29sZF9zaG9jayBkb21haW4KIyBDYW4ndCBmaW5kIFRSQVAgcHJvdGVpbiAodHJwIFJOQS1CaW5kaW5nIGF0dGVudWF0aW9uIHByb3RlaW4pCiMgSGF2ZSBhZGRlZCBuZXcgUkJEcyBmcm9tIENhc3RlbGxvJ3MgMjAxNiBwYXBlciAtIFRoaW9yZWRveGluLCBQRFosIERaRiwgCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKcmJkLmRvbXMgPSBjKCJSUk1fZG9tIiwiS0hfZG9tIiwiZHNSQkRfZG9tIiwiWm5mX0NDQ0giLCJabmZfQzJIMiIsIlpuZl9DQ0hDIiwiUzFfZG9tIiwiUEFaX2RvbSIsIlBpd2kiLCJOdWNsZW90aWRlLWJkX2EvYl9wbGFpdCIsIkNTRCIsIkNvbGQtc2hvY2tfQ1MiLCJQdW1pbGlvX1JOQS1iZF9ycHQiLCJTQU07IiwiU0FNL3BvaW50ZWQiLCJERUFEIiwiVGhpb3JlZG94aW4iLCJQRFoiLCJEWkZfZG9tIikKcmJkLmRvbXMKCiMgQW5ub3RhdGluZyBib3RoIGdlaWdlciBhbmQgdXYtZG9zYWdlIGRhdGFzZXRzIHdpdGgga25vd24gInJiZCIgZG9tYWlucyBhbmQgaG93IG1hbnkgb2YgdGhlc2UgUkJEIGRvbWFpbnMgZWFjaCBwcm90ZWluIGhhcy4KdXYucW0kbnVtLnJiZHMgPSByb3dTdW1zKHNhcHBseShyYmQuZG9tcywgZnVuY3Rpb24oeCkgZ3JlcGwoeCwgdXYucW0kZG9tYWlucykpKQp1di5xbSR3aGljaC5yYmQgPSBhcHBseShzYXBwbHkocmJkLmRvbXMsIGZ1bmN0aW9uKHgpIGdyZXBsKHgsIHV2LnFtJGRvbWFpbnMpKSwxLCBmdW5jdGlvbih5KSBwYXN0ZShuYW1lcyh3aGljaCh5PT1UKSksY29sbGFwc2U9IjsgIikpCiNoZWFkKHV2LnFtW3doaWNoKHV2LnFtJG51bS5yYmRzICE9IDApLF0sbj0xMCkKCmdlaWdlci5xbSRudW0ucmJkcyA9IHJvd1N1bXMoc2FwcGx5KHJiZC5kb21zLCBmdW5jdGlvbih4KSBncmVwbCh4LCBnZWlnZXIucW0kZG9tYWlucykpKQpnZWlnZXIucW0kd2hpY2gucmJkID0gYXBwbHkoc2FwcGx5KHJiZC5kb21zLCBmdW5jdGlvbih4KSBncmVwbCh4LCBnZWlnZXIucW0kZG9tYWlucykpLDEsIGZ1bmN0aW9uKHkpIHBhc3RlKG5hbWVzKHdoaWNoKHk9PVQpKSxjb2xsYXBzZT0iOyAiKSkKI2hlYWQoZ2VpZ2VyLnFtW3doaWNoKGdlaWdlci5xbSRudW0ucmJkcyAhPSAwKSxdLG49MTApCgoKIyBQcmludGluZyB3aGF0IHBlcmNlbnRhZ2UgIG9mIGVhY2ggbGlzdCBpcyBhbm5vdGF0ZWQgd2l0aCBSTkEgYmluZGluZyBkb21haW5zCm9iai5uYW1lcyA9IGMoIm9saWdvLmNsLmluLm5jIiwib2xpZ28uY2wiLCJvbGlnby5uYyIsImNsLmFuZC5uYy5wcm90IikKY291bnQgPSAwCgpmb3IoeSBpbiBsaXN0KG9saWdvLmNsLmluLm5jLG9saWdvLmNsLG9saWdvLm5jLGNsLmFuZC5uYy5wcm90KSl7CiAgY291bnQgPSBjb3VudCsxCiAgeSA9IHF1ZXJ5TWFueSh5LHNjb3Blcz0idW5pcHJvdCIsZmllbGRzPWMoImVuc2VtYmwiLCJuYW1lIiwic3ltYm9sIiwiaW50ZXJwcm8iLCJnbyIpKQogIHkkZG9tYWlucyA9IHNhcHBseShzYXBwbHkoeSRpbnRlcnBybywiW1siLDMpLGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2U9IjsgIikpCiAgeSRudW0ucmJkcyA9IHJvd1N1bXMoc2FwcGx5KHJiZC5kb21zLCBmdW5jdGlvbih4KSBncmVwbCh4LCB5JGRvbWFpbnMpKSkKICB5JHdoaWNoLnJiZCA9IGFwcGx5KHNhcHBseShyYmQuZG9tcywgZnVuY3Rpb24oeCkgZ3JlcGwoeCwgeSRkb21haW5zKSksMSwgZnVuY3Rpb24oeikgcGFzdGUobmFtZXMod2hpY2goej09VCkpLGNvbGxhcHNlPSI7ICIpKQogIHByaW50KHRhYmxlKHkkbnVtLnJiZHMpKQogIHByaW50KHBhc3RlKCJQZXJjZW50YWdlIG9mIHByb3RlaW5zIHdpdGggUk5BLWJpbmRpbmcgZG9tYWlucyBpbiAiLCBvYmoubmFtZXNbY291bnRdLCIgPSAiLHJvdW5kKDEwMCpzdW0odGFibGUoeSRudW0ucmJkcylbMjo0XSkvc3VtKHRhYmxlKHkkbnVtLnJiZHMpKSwyKSxzZXA9IiIpKQp9CmBgYAoKYGBge3IgMTBfSW50ZXJzZWN0LW9mLVRyaXpvbC1vbGlnb2RUfQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMCA6IExvb2tpbmcgYXQgdGhlIGludGVyc2VjdCBvZiBwcm90ZWlucyBiZXR3ZWVuIG9saWdvZFQgYW5kIFRyaXpvbAojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVXNpbmcgVHJpem9sIG1hcHBlZCB0byBoZ25jX3N5bWJvbCBhcyBvbGlnb2RUIGlzIG9ubHkgaW4gc3ltYm9scy4KbGlicmFyeSh2ZW5uKQpUcml6b2wgPSB1bmlxdWUodXYucW0kcXVlcnkpCm9saWdvZFQgPSB1bmlxdWUob2xpZ28uY2wpCnYgPSB2ZW5uKGxpc3QoVHJpem9sPVRyaXpvbCxvbGlnb2RUPW9saWdvZFQpLGludGVyc2VjdGlvbnM9VCkKCmJvdGggPSBhdHRyKHYsImludGVyc2VjdGlvbiIpJGBUcml6b2w6b2xpZ29kVGAgIyBuID0gMjk4Cm9saWdvLm9ubHkgPSBhdHRyKHYsImludGVyc2VjdGlvbiIpJGBvbGlnb2RUYCAjIG4gPSAxOTEKdHJpem9sLm9ubHkgPSBhdHRyKHYsImludGVyc2VjdGlvbiIpJGBUcml6b2xgICMgbiA9IDE0NDYKCiMgRW5yaWNobWVudCBmb3Igb3ZlcmxhcHMgYW5kIHNldGRpZmZzCm0gPSBjKCJib3RoIiwib2xpZ28tb25seSIsInRyaXpvbC1vbmx5IikKYyA9IDAKZm9yKHQgaW4gbGlzdChib3RoPWJvdGgsb2xpZ28ub25seT1vbGlnby5vbmx5LHRyaXpvbC5vbmx5PXRyaXpvbC5vbmx5KSl7CiAgYz1jKzEKICB0LmVucmljaC5nbyA9IHJ1bkdvc2VxKHQsYmlhcy5kZiwgYmlhcy5kZiRwcm90YmlhcyxnZWlnZXIuY2F0LmdvKQogIHQuZW5yaWNoLmludGVycHJvID0gcnVuR29zZXEodCxiaWFzLmRmLCBiaWFzLmRmJHByb3RiaWFzLGdlaWdlci5jYXQpCiAgd3JpdGUudGFibGUodC5lbnJpY2guZ29bWzJdXVssYygxLDY6Nyw0OjUsMiw4KV0scGFzdGUob3V0ZGlyLHBhc3RlKG1bY10sIi1nZW5lcy1HTy1lbnJpY2htZW50LnR4dCIsc2VwPSIiKSxzZXA9Ii8iKSxzZXA9Ilx0Iixyb3cubmFtZXM9RixxdW90ZT1GKQogIHdyaXRlLnRhYmxlKHQuZW5yaWNoLmludGVycHJvW1syXV0scGFzdGUob3V0ZGlyLCBwYXN0ZShtW2NdLCItZ2VuZXMtSW50ZXJwcm8tZW5yaWNobWVudC50eHQiLHNlcD0iIiksc2VwPSIvIiksc2VwPSJcdCIscm93Lm5hbWVzPUYscXVvdGU9RikKfQoKb28gPSBpbnRlcnNlY3Qob2xpZ28ub25seSxnZWlnZXIucW0kcXVlcnkpICMgMTcwLzE5MQp0dCA9IGludGVyc2VjdCh0cml6b2wub25seSxnZWlnZXIucW0kcXVlcnkpICMgMTE0NC8xNDQ2CmJsID0gaW50ZXJzZWN0KGJvdGgsZ2VpZ2VyLnFtJHF1ZXJ5KSAjIDI5Ny8yOTggbWlzaW5nIEVJRjNDL1E5OTYxMwpgYGAKCmBgYHtyIDExX0RldG91ci1jb21wYXJpbmctb3RoZXItVTJPUy1kYXRhc2V0c30KCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBTdGVwIDExIDogQ29tcGFyaW5nIDMgZGlmZmVyZW50IFUyT1MgcHJvdGVvbWljcyBkYXRhc2V0cyBmcm9tIHRoZSBjdXJyZW50IGRlY2FkZQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpnZWlnZXIgPSBhcy5jaGFyYWN0ZXIodW5pcXVlKGZEYXRhKGFnZy51Mm9zKSRtYXN0ZXJfcHJvdGVpbikpCgpiZWNrID0gcmVhZC50YWJsZSgiSW5wdXQvQmVjazIwMTFfbjU3ODEudHh0IikKYmVjayA9IGFzLmNoYXJhY3RlcihiZWNrJFYxKQoKbHVuZGJlcmcuZW5zID0gcmVhZC50YWJsZSgiSW5wdXQvTHVuZGJlcmcyMDEwX241NDgwLnR4dCIpCmx1bmRiZXJnID0gdW5pcXVlKGJpdHIoYXMuY2hhcmFjdGVyKGx1bmRiZXJnLmVucyRWMSksZnJvbVR5cGU9IkVOU0VNQkwiLCB0b1R5cGU9IlVOSVBST1QiLCBPcmdEYj0ib3JnLkhzLmVnLmRiIikkVU5JUFJPVCkKbGVuZ3RoKGx1bmRiZXJnKQoKbGlicmFyeSh2ZW5uKQpsaWJyYXJ5KGdwbG90cykKbGlicmFyeShsaW1tYSkKCnZlbm4udTJvcyA9IHZlbm4obGlzdChHZWlnZXIyMDEyPWdlaWdlcixCZWNrMjAxMT1iZWNrLEx1bmRiZXJnMjAxMD1sdW5kYmVyZykpCgpgYGAKU21hbGwgZXhlcmNpc2Ugb24gaG93IG11Y2ggR2VpZ2VyIGV0IGFsLCBCZWNrIGV0IGFsLCBhbmQgTHVuZGJlcmcgZXQgYWwuLCBwcm90ZWluIHNldHMgZnJvbSBNYXNzIFNwZWN0cm9tZXRlcnkgZXhwZXJpbWVudHMgb3ZlcmxhcC4gV2l0aCBMdW5kYmVyZyBldCBhbC4sIEkgaGFkIHRvIG1hcCBFbnNlbWJsIElEcyB0byBVTklQUk9UIGFuZCB0aGVuIGRvIHRoZSBjb21wYXJpc29uIHdoaWNoIGNhdXNlZCBhIG9uZS10by1tYW55IG1hcHBpbmcuIFRoZSBleGNlc3MgNDk0MSBvbmx5IGZvdW5kIGluIEx1bmRiZXJnIGV0IGFsIGlzIGFsbW9zdCBleGNsdXNpdmVseSBkdWUgdG8gdGhlIG1hcHBpbmcgb2Ygb25lIEVuc2VtYmwgSUQgdG8gbWFueSBVTklQUk9UIElEcy4gR2VpZ2VyIGV0IGFsLCBiZWluZyB0aGUgbW9zdCByZWNlbnQgc3R1ZHkgZG9lcyBjbGFpbSB0byBoYXZlIG1heGltYWwgcHJvdGVpbiBtYXBwaW5nIGZvciBVMk9TLiBCZWNrIGV0IGFsLiwgCgpTaG91bGQgd2UgdXNlIGp1c3QgdGhlIDQxNDUgdGhhdCBvdmVybGFwcyBhcyBvdXIgaGlnaCBjb25maWRlbnQgc2V0IG9yIGZvY3VzIG9uIEdlaWdlciBhcyBpdCB0aGUgbW9zdCByZWNlbnQgPyAKCg==